Issue #207 - Final removal of BlockheadClient
This commit is contained in:
parent
7f345fc2d4
commit
ae51344582
|
@ -14,7 +14,6 @@ org.eclipse.jetty.websocket.common.io.AbstractWebSocketConnection.LEVEL=DEBUG
|
|||
# org.eclipse.jetty.websocket.common.Generator.LEVEL=DEBUG
|
||||
org.eclipse.jetty.websocket.common.Parser.LEVEL=DEBUG
|
||||
# org.eclipse.jetty.websocket.client.TrackingSocket.LEVEL=DEBUG
|
||||
# org.eclipse.jetty.websocket.common.test.BlockheadServerConnection.LEVEL=DEBUG
|
||||
|
||||
### Hide the stacktraces during testing
|
||||
org.eclipse.jetty.websocket.client.internal.io.UpgradeConnection.STACKS=false
|
||||
|
|
|
@ -1,72 +0,0 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// All rights reserved. This program and the accompanying materials
|
||||
// are made available under the terms of the Eclipse Public License v1.0
|
||||
// and Apache License v2.0 which accompanies this distribution.
|
||||
//
|
||||
// The Eclipse Public License is available at
|
||||
// http://www.eclipse.org/legal/epl-v10.html
|
||||
//
|
||||
// The Apache License v2.0 is available at
|
||||
// http://www.opensource.org/licenses/apache2.0.php
|
||||
//
|
||||
// You may elect to redistribute this code under either of these licenses.
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.websocket.common.test;
|
||||
|
||||
import static org.hamcrest.Matchers.is;
|
||||
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.Parameterized;
|
||||
import org.junit.runners.Parameterized.Parameters;
|
||||
|
||||
/**
|
||||
* Gotta test some basic constructors of the BlockheadClient.
|
||||
*/
|
||||
@RunWith(value = Parameterized.class)
|
||||
public class BlockheadClientConstructionTest
|
||||
{
|
||||
@Parameters
|
||||
public static Collection<Object[]> data()
|
||||
{
|
||||
List<Object[]> data = new ArrayList<>();
|
||||
// @formatter:off
|
||||
data.add(new Object[] { "ws://localhost/", "http://localhost/" });
|
||||
data.add(new Object[] { "ws://localhost:8080/", "http://localhost:8080/" });
|
||||
data.add(new Object[] { "ws://webtide.com/", "http://webtide.com/" });
|
||||
data.add(new Object[] { "ws://www.webtide.com/sockets/chat", "http://www.webtide.com/sockets/chat" });
|
||||
// @formatter:on
|
||||
return data;
|
||||
}
|
||||
|
||||
private URI expectedWsUri;
|
||||
private URI expectedHttpUri;
|
||||
|
||||
public BlockheadClientConstructionTest(String wsuri, String httpuri)
|
||||
{
|
||||
this.expectedWsUri = URI.create(wsuri);
|
||||
this.expectedHttpUri = URI.create(httpuri);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testURIs() throws URISyntaxException
|
||||
{
|
||||
@SuppressWarnings("resource")
|
||||
XBlockheadClient client = new XBlockheadClient(expectedWsUri);
|
||||
Assert.assertThat("Websocket URI",client.getWebsocketURI(),is(expectedWsUri));
|
||||
Assert.assertThat("Websocket URI",client.getHttpURI(),is(expectedHttpUri));
|
||||
}
|
||||
|
||||
}
|
|
@ -1,79 +0,0 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// All rights reserved. This program and the accompanying materials
|
||||
// are made available under the terms of the Eclipse Public License v1.0
|
||||
// and Apache License v2.0 which accompanies this distribution.
|
||||
//
|
||||
// The Eclipse Public License is available at
|
||||
// http://www.eclipse.org/legal/epl-v10.html
|
||||
//
|
||||
// The Apache License v2.0 is available at
|
||||
// http://www.opensource.org/licenses/apache2.0.php
|
||||
//
|
||||
// You may elect to redistribute this code under either of these licenses.
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.websocket.common.test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.eclipse.jetty.toolchain.test.EventQueue;
|
||||
import org.eclipse.jetty.websocket.common.WebSocketFrame;
|
||||
|
||||
/**
|
||||
* Interface for BlockheadClient.
|
||||
*/
|
||||
public interface IBlockheadClient extends AutoCloseable
|
||||
{
|
||||
public void addExtensions(String xtension);
|
||||
|
||||
public void addHeader(String header);
|
||||
|
||||
public boolean awaitDisconnect(long timeout, TimeUnit unit) throws InterruptedException;
|
||||
|
||||
public void close();
|
||||
|
||||
public void close(int statusCode, String message);
|
||||
|
||||
public void connect() throws IOException;
|
||||
|
||||
public void disconnect();
|
||||
|
||||
public void expectServerDisconnect();
|
||||
|
||||
public HttpResponse expectUpgradeResponse() throws IOException;
|
||||
|
||||
public InetSocketAddress getLocalSocketAddress();
|
||||
|
||||
public String getProtocols();
|
||||
|
||||
public InetSocketAddress getRemoteSocketAddress();
|
||||
|
||||
public EventQueue<WebSocketFrame> readFrames(int expectedFrameCount, int timeoutDuration, TimeUnit timeoutUnit) throws Exception;
|
||||
|
||||
public HttpResponse readResponseHeader() throws IOException;
|
||||
|
||||
public void sendStandardRequest() throws IOException;
|
||||
|
||||
public void setConnectionValue(String connectionValue);
|
||||
|
||||
public void setProtocols(String protocols);
|
||||
|
||||
public void setTimeout(int duration, TimeUnit unit);
|
||||
|
||||
public void write(WebSocketFrame frame) throws IOException;
|
||||
|
||||
public void writeRaw(ByteBuffer buf) throws IOException;
|
||||
|
||||
public void writeRaw(ByteBuffer buf, int numBytes) throws IOException;
|
||||
|
||||
public void writeRaw(String str) throws IOException;
|
||||
|
||||
public void writeRawSlowly(ByteBuffer buf, int segmentSize) throws IOException;
|
||||
}
|
|
@ -1,827 +0,0 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// All rights reserved. This program and the accompanying materials
|
||||
// are made available under the terms of the Eclipse Public License v1.0
|
||||
// and Apache License v2.0 which accompanies this distribution.
|
||||
//
|
||||
// The Eclipse Public License is available at
|
||||
// http://www.eclipse.org/legal/epl-v10.html
|
||||
//
|
||||
// The Apache License v2.0 is available at
|
||||
// http://www.opensource.org/licenses/apache2.0.php
|
||||
//
|
||||
// You may elect to redistribute this code under either of these licenses.
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.websocket.common.test;
|
||||
|
||||
import static org.hamcrest.Matchers.anyOf;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.hamcrest.Matchers.notNullValue;
|
||||
|
||||
import java.io.EOFException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.Socket;
|
||||
import java.net.SocketAddress;
|
||||
import java.net.SocketTimeoutException;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import javax.net.ssl.HttpsURLConnection;
|
||||
|
||||
import org.eclipse.jetty.io.ByteBufferPool;
|
||||
import org.eclipse.jetty.io.MappedByteBufferPool;
|
||||
import org.eclipse.jetty.toolchain.test.EventQueue;
|
||||
import org.eclipse.jetty.util.BufferUtil;
|
||||
import org.eclipse.jetty.util.IO;
|
||||
import org.eclipse.jetty.util.StringUtil;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
import org.eclipse.jetty.websocket.api.BatchMode;
|
||||
import org.eclipse.jetty.websocket.api.FrameCallback;
|
||||
import org.eclipse.jetty.websocket.api.WebSocketPolicy;
|
||||
import org.eclipse.jetty.websocket.api.extensions.ExtensionConfig;
|
||||
import org.eclipse.jetty.websocket.api.extensions.Frame;
|
||||
import org.eclipse.jetty.websocket.api.extensions.IncomingFrames;
|
||||
import org.eclipse.jetty.websocket.api.extensions.OutgoingFrames;
|
||||
import org.eclipse.jetty.websocket.api.util.WSURI;
|
||||
import org.eclipse.jetty.websocket.common.AcceptHash;
|
||||
import org.eclipse.jetty.websocket.common.CloseInfo;
|
||||
import org.eclipse.jetty.websocket.common.Generator;
|
||||
import org.eclipse.jetty.websocket.common.OpCode;
|
||||
import org.eclipse.jetty.websocket.common.Parser;
|
||||
import org.eclipse.jetty.websocket.common.WebSocketFrame;
|
||||
import org.eclipse.jetty.websocket.common.extensions.ExtensionStack;
|
||||
import org.eclipse.jetty.websocket.common.extensions.WebSocketExtensionFactory;
|
||||
import org.eclipse.jetty.websocket.common.io.http.HttpResponseHeaderParser;
|
||||
import org.eclipse.jetty.websocket.common.scopes.SimpleContainerScope;
|
||||
import org.junit.Assert;
|
||||
|
||||
/**
|
||||
* A simple websocket client for performing unit tests with.
|
||||
* <p>
|
||||
* This client will use {@link HttpURLConnection} and {@link HttpsURLConnection} with standard blocking calls to perform websocket requests.
|
||||
* <p>
|
||||
* This client is <u>NOT</u> intended to be performant or follow the websocket spec religiously. In fact, being able to deviate from the websocket spec at will
|
||||
* is desired for this client to operate properly for the unit testing within this module.
|
||||
* <p>
|
||||
* The BlockheadClient should never validate frames or bytes being sent for validity, against any sort of spec, or even sanity. It should, however be honest
|
||||
* with regards to basic IO behavior, a write should work as expected, a read should work as expected, but <u>what</u> byte it sends or reads is not within its
|
||||
* scope.
|
||||
*/
|
||||
public class XBlockheadClient implements OutgoingFrames, AutoCloseable, IBlockheadClient, Parser.Handler
|
||||
{
|
||||
private class FrameReadingThread extends Thread implements Runnable, IncomingFrames
|
||||
{
|
||||
public long totalBytes = 0;
|
||||
public long totalReadOps = 0;
|
||||
public long totalParseOps = 0;
|
||||
|
||||
public EventQueue<WebSocketFrame> frames = new EventQueue<>();
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
LOG.debug("Reading frames from server");
|
||||
|
||||
byte buf[] = new byte[BUFFER_SIZE];
|
||||
try
|
||||
{
|
||||
if ((remainingBuffer != null) && (remainingBuffer.remaining() > 0))
|
||||
{
|
||||
LOG.debug("Reading bytes received during response header parse: {}",BufferUtil.toDetailString(remainingBuffer));
|
||||
totalBytes += remainingBuffer.remaining();
|
||||
totalReadOps++;
|
||||
parser.parse(remainingBuffer);
|
||||
}
|
||||
|
||||
int len = 0;
|
||||
int available = 0;
|
||||
while (!eof)
|
||||
{
|
||||
available = in.available();
|
||||
len = in.read(buf,0,Math.min(available,buf.length));
|
||||
totalReadOps++;
|
||||
if (len < 0)
|
||||
{
|
||||
eof = true;
|
||||
break;
|
||||
}
|
||||
else if (len > 0)
|
||||
{
|
||||
totalBytes += len;
|
||||
ByteBuffer bbuf = ByteBuffer.wrap(buf,0,len);
|
||||
if (LOG.isDebugEnabled())
|
||||
{
|
||||
LOG.debug("Read {} bytes: {}",len,BufferUtil.toDetailString(bbuf));
|
||||
}
|
||||
totalParseOps++;
|
||||
parser.parse(bbuf);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
LOG.debug(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
StringBuilder str = new StringBuilder();
|
||||
str.append("FrameReadingThread[");
|
||||
str.append(",frames=" + frames.size());
|
||||
str.append(String.format(",totalBytes=%,d",totalBytes));
|
||||
str.append(String.format(",totalReadOps=%,d",totalReadOps));
|
||||
str.append(String.format(",totalParseOps=%,d",totalParseOps));
|
||||
str.append("]");
|
||||
return str.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void incomingFrame(Frame frame, FrameCallback callback)
|
||||
{
|
||||
this.frames.add(WebSocketFrame.copy(frame));
|
||||
}
|
||||
|
||||
public synchronized void clear()
|
||||
{
|
||||
this.frames.clear();
|
||||
}
|
||||
}
|
||||
|
||||
private static final String REQUEST_HASH_KEY = "dGhlIHNhbXBsZSBub25jZQ==";
|
||||
private static final int BUFFER_SIZE = 64 * 1024;
|
||||
private static final Logger LOG = Log.getLogger(XBlockheadClient.class);
|
||||
private final URI destHttpURI;
|
||||
private final URI destWebsocketURI;
|
||||
private final ByteBufferPool bufferPool;
|
||||
private final Generator generator;
|
||||
private final Parser parser;
|
||||
|
||||
private final WebSocketExtensionFactory extensionFactory;
|
||||
private FrameReadingThread frameReader;
|
||||
|
||||
private ExecutorService executor;
|
||||
private Socket socket;
|
||||
private OutputStream out;
|
||||
private InputStream in;
|
||||
private int version = 13; // default to RFC-6455
|
||||
private String protocols;
|
||||
private List<String> extensions = new ArrayList<>();
|
||||
private List<String> headers = new ArrayList<>();
|
||||
private byte[] clientmask = new byte[] { (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF };
|
||||
private int timeout = 1000;
|
||||
private OutgoingFrames outgoing = this;
|
||||
private boolean eof = false;
|
||||
private ExtensionStack extensionStack;
|
||||
private CountDownLatch disconnectedLatch = new CountDownLatch(1);
|
||||
private ByteBuffer remainingBuffer;
|
||||
|
||||
private String connectionValue = "Upgrade";
|
||||
|
||||
public XBlockheadClient(URI destWebsocketURI) throws URISyntaxException
|
||||
{
|
||||
this(WebSocketPolicy.newClientPolicy(),destWebsocketURI);
|
||||
}
|
||||
|
||||
public XBlockheadClient(WebSocketPolicy policy, URI destWebsocketURI) throws URISyntaxException
|
||||
{
|
||||
Assert.assertThat("Websocket URI scheme",destWebsocketURI.getScheme(),anyOf(is("ws"),is("wss")));
|
||||
this.destWebsocketURI = destWebsocketURI;
|
||||
if (destWebsocketURI.getScheme().equals("wss"))
|
||||
{
|
||||
throw new RuntimeException("Sorry, BlockheadClient does not support SSL");
|
||||
}
|
||||
this.destHttpURI = WSURI.toHttp(destWebsocketURI);
|
||||
|
||||
LOG.debug("WebSocket URI: {}",destWebsocketURI);
|
||||
LOG.debug(" HTTP URI: {}",destHttpURI);
|
||||
|
||||
// This is a blockhead client, no point tracking leaks on this object.
|
||||
this.bufferPool = new MappedByteBufferPool(8192);
|
||||
this.generator = new Generator(policy,bufferPool);
|
||||
this.parser = new Parser(policy,bufferPool,this);
|
||||
|
||||
this.extensionFactory = new WebSocketExtensionFactory(new SimpleContainerScope(policy,bufferPool));
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.eclipse.jetty.websocket.common.test.IBlockheadClient#addExtensions(java.lang.String)
|
||||
*/
|
||||
@Override
|
||||
public void addExtensions(String xtension)
|
||||
{
|
||||
this.extensions.add(xtension);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.eclipse.jetty.websocket.common.test.IBlockheadClient#addHeader(java.lang.String)
|
||||
*/
|
||||
@Override
|
||||
public void addHeader(String header)
|
||||
{
|
||||
this.headers.add(header);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.eclipse.jetty.websocket.common.test.IBlockheadClient#awaitDisconnect(long, java.util.concurrent.TimeUnit)
|
||||
*/
|
||||
@Override
|
||||
public boolean awaitDisconnect(long timeout, TimeUnit unit) throws InterruptedException
|
||||
{
|
||||
return disconnectedLatch.await(timeout,unit);
|
||||
}
|
||||
|
||||
public void clearCaptured()
|
||||
{
|
||||
frameReader.clear();
|
||||
}
|
||||
|
||||
public void clearExtensions()
|
||||
{
|
||||
extensions.clear();
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.eclipse.jetty.websocket.common.test.IBlockheadClient#close()
|
||||
*/
|
||||
@Override
|
||||
public void close()
|
||||
{
|
||||
LOG.debug("close()");
|
||||
close(-1,null);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.eclipse.jetty.websocket.common.test.IBlockheadClient#close(int, java.lang.String)
|
||||
*/
|
||||
@Override
|
||||
public void close(int statusCode, String message)
|
||||
{
|
||||
LOG.debug("close({},{})",statusCode,message);
|
||||
CloseInfo close = new CloseInfo(statusCode,message);
|
||||
|
||||
// if (!ioState.isClosed())
|
||||
// {
|
||||
// ioState.onCloseLocal(close);
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// LOG.debug("Not issuing close. ioState = {}",ioState);
|
||||
// }
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.eclipse.jetty.websocket.common.test.IBlockheadClient#connect()
|
||||
*/
|
||||
@Override
|
||||
public void connect() throws IOException
|
||||
{
|
||||
InetAddress destAddr = InetAddress.getByName(destHttpURI.getHost());
|
||||
int port = destHttpURI.getPort();
|
||||
|
||||
SocketAddress endpoint = new InetSocketAddress(destAddr,port);
|
||||
|
||||
socket = new Socket();
|
||||
socket.setSoTimeout(timeout);
|
||||
socket.connect(endpoint);
|
||||
|
||||
out = socket.getOutputStream();
|
||||
in = socket.getInputStream();
|
||||
}
|
||||
|
||||
public void disconnect()
|
||||
{
|
||||
LOG.debug("disconnect");
|
||||
IO.close(in);
|
||||
IO.close(out);
|
||||
disconnectedLatch.countDown();
|
||||
if (frameReader != null)
|
||||
{
|
||||
frameReader.interrupt();
|
||||
}
|
||||
if (socket != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
socket.close();
|
||||
}
|
||||
catch (IOException ignore)
|
||||
{
|
||||
/* ignore */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.eclipse.jetty.websocket.common.test.IBlockheadClient#expectServerDisconnect()
|
||||
*/
|
||||
@Override
|
||||
public void expectServerDisconnect()
|
||||
{
|
||||
if (eof)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
int len = in.read();
|
||||
if (len == (-1))
|
||||
{
|
||||
// we are disconnected
|
||||
eof = true;
|
||||
return;
|
||||
}
|
||||
|
||||
Assert.assertThat("Expecting no data and proper socket disconnect (issued from server)",len,is(-1));
|
||||
}
|
||||
catch (SocketTimeoutException e)
|
||||
{
|
||||
LOG.warn(e);
|
||||
Assert.fail("Expected a server initiated disconnect, instead the read timed out");
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
// acceptable path
|
||||
}
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.eclipse.jetty.websocket.common.test.IBlockheadClient#expectUpgradeResponse()
|
||||
*/
|
||||
@Override
|
||||
public HttpResponse expectUpgradeResponse() throws IOException
|
||||
{
|
||||
HttpResponse response = readResponseHeader();
|
||||
|
||||
if (LOG.isDebugEnabled())
|
||||
{
|
||||
LOG.debug("Response Header: {}{}",'\n',response);
|
||||
}
|
||||
|
||||
Assert.assertThat("Response Status Code",response.getStatusCode(),is(101));
|
||||
Assert.assertThat("Response Status Reason",response.getStatusReason(),is("Switching Protocols"));
|
||||
Assert.assertThat("Response Header[Upgrade]",response.getHeader("Upgrade"),is("WebSocket"));
|
||||
Assert.assertThat("Response Header[Connection]",response.getHeader("Connection"),is("Upgrade"));
|
||||
|
||||
// Validate the Sec-WebSocket-Accept
|
||||
String acceptKey = response.getHeader("Sec-WebSocket-Accept");
|
||||
Assert.assertThat("Response Header[Sec-WebSocket-Accept Exists]",acceptKey,notNullValue());
|
||||
|
||||
String reqKey = REQUEST_HASH_KEY;
|
||||
String expectedHash = AcceptHash.hashKey(reqKey);
|
||||
|
||||
Assert.assertThat("Valid Sec-WebSocket-Accept Hash?",acceptKey,is(expectedHash));
|
||||
|
||||
// collect extensions configured in response header
|
||||
List<ExtensionConfig> configs = getExtensionConfigs(response);
|
||||
extensionStack = new ExtensionStack(this.extensionFactory);
|
||||
extensionStack.negotiate(configs);
|
||||
|
||||
// Setup Frame Reader
|
||||
this.frameReader = new FrameReadingThread();
|
||||
this.frameReader.start();
|
||||
|
||||
// Start with default routing
|
||||
extensionStack.setNextIncoming(frameReader); // the websocket layer
|
||||
extensionStack.setNextOutgoing(outgoing); // the network layer
|
||||
|
||||
// Configure Parser / Generator
|
||||
extensionStack.configure(parser);
|
||||
extensionStack.configure(generator);
|
||||
|
||||
// Start Stack
|
||||
try
|
||||
{
|
||||
extensionStack.start();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new IOException("Unable to start Extension Stack");
|
||||
}
|
||||
|
||||
// configure parser
|
||||
// ioState.onOpened();
|
||||
|
||||
LOG.debug("outgoing = {}",outgoing);
|
||||
LOG.debug("incoming = {}",extensionStack);
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
public void flush() throws IOException
|
||||
{
|
||||
out.flush();
|
||||
}
|
||||
|
||||
public String getConnectionValue()
|
||||
{
|
||||
return connectionValue;
|
||||
}
|
||||
|
||||
public ExecutorService getExecutor()
|
||||
{
|
||||
if (executor == null)
|
||||
{
|
||||
executor = Executors.newCachedThreadPool();
|
||||
}
|
||||
return executor;
|
||||
}
|
||||
|
||||
private List<ExtensionConfig> getExtensionConfigs(HttpResponse response)
|
||||
{
|
||||
List<ExtensionConfig> configs = new ArrayList<>();
|
||||
|
||||
String econf = response.getHeader("Sec-WebSocket-Extensions");
|
||||
if (econf != null)
|
||||
{
|
||||
LOG.debug("Found Extension Response: {}",econf);
|
||||
ExtensionConfig config = ExtensionConfig.parse(econf);
|
||||
configs.add(config);
|
||||
}
|
||||
return configs;
|
||||
}
|
||||
|
||||
public List<String> getExtensions()
|
||||
{
|
||||
return extensions;
|
||||
}
|
||||
|
||||
public URI getHttpURI()
|
||||
{
|
||||
return destHttpURI;
|
||||
}
|
||||
|
||||
public InetSocketAddress getLocalSocketAddress()
|
||||
{
|
||||
return (InetSocketAddress)socket.getLocalSocketAddress();
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.eclipse.jetty.websocket.common.test.IBlockheadClient#getProtocols()
|
||||
*/
|
||||
@Override
|
||||
public String getProtocols()
|
||||
{
|
||||
return protocols;
|
||||
}
|
||||
|
||||
|
||||
public InetSocketAddress getRemoteSocketAddress()
|
||||
{
|
||||
return (InetSocketAddress)socket.getRemoteSocketAddress();
|
||||
}
|
||||
|
||||
public String getRequestHost()
|
||||
{
|
||||
if (destHttpURI.getPort() > 0)
|
||||
{
|
||||
return String.format("%s:%d",destHttpURI.getHost(),destHttpURI.getPort());
|
||||
}
|
||||
else
|
||||
{
|
||||
return destHttpURI.getHost();
|
||||
}
|
||||
}
|
||||
|
||||
public String getRequestPath()
|
||||
{
|
||||
StringBuilder path = new StringBuilder();
|
||||
path.append(destHttpURI.getPath());
|
||||
if (StringUtil.isNotBlank(destHttpURI.getQuery()))
|
||||
{
|
||||
path.append('?').append(destHttpURI.getQuery());
|
||||
}
|
||||
return path.toString();
|
||||
}
|
||||
|
||||
public String getRequestWebSocketKey()
|
||||
{
|
||||
return REQUEST_HASH_KEY;
|
||||
}
|
||||
|
||||
public String getRequestWebSocketOrigin()
|
||||
{
|
||||
return destWebsocketURI.toASCIIString();
|
||||
}
|
||||
|
||||
public int getVersion()
|
||||
{
|
||||
return version;
|
||||
}
|
||||
|
||||
public URI getWebsocketURI()
|
||||
{
|
||||
return destWebsocketURI;
|
||||
}
|
||||
|
||||
public boolean isConnected()
|
||||
{
|
||||
return (socket != null) && (socket.isConnected());
|
||||
}
|
||||
|
||||
// @Override
|
||||
// public void onConnectionStateChange(ConnectionState state)
|
||||
// {
|
||||
// LOG.debug("CLIENT onConnectionStateChange() - {}",state);
|
||||
// switch (state)
|
||||
// {
|
||||
// case CLOSED:
|
||||
// // Per Spec, client should not initiate disconnect on its own
|
||||
// // this.disconnect();
|
||||
// break;
|
||||
// case CLOSING:
|
||||
// CloseInfo close = ioState.getCloseInfo();
|
||||
//
|
||||
// WebSocketFrame frame = close.asFrame();
|
||||
// LOG.debug("Issuing: {}",frame);
|
||||
// try
|
||||
// {
|
||||
// write(frame);
|
||||
// }
|
||||
// catch (IOException e)
|
||||
// {
|
||||
// LOG.debug(e);
|
||||
// }
|
||||
// break;
|
||||
// default:
|
||||
// /* do nothing */
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
|
||||
@Override
|
||||
public void outgoingFrame(Frame frame, FrameCallback callback, BatchMode batchMode)
|
||||
{
|
||||
ByteBuffer headerBuf = generator.generateHeaderBytes(frame);
|
||||
if (LOG.isDebugEnabled())
|
||||
{
|
||||
LOG.debug("writing out: {}",BufferUtil.toDetailString(headerBuf));
|
||||
}
|
||||
try
|
||||
{
|
||||
BufferUtil.writeTo(headerBuf,out);
|
||||
BufferUtil.writeTo(frame.getPayload(),out);
|
||||
out.flush();
|
||||
if (callback != null)
|
||||
{
|
||||
callback.succeed();
|
||||
}
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
if (callback != null)
|
||||
{
|
||||
callback.fail(e);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
bufferPool.release(headerBuf);
|
||||
}
|
||||
|
||||
if (frame.getOpCode() == OpCode.CLOSE)
|
||||
{
|
||||
disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onFrame(Frame frame)
|
||||
{
|
||||
extensionStack.incomingFrame(frame, new FrameCallback.Adapter());
|
||||
return true;
|
||||
}
|
||||
|
||||
public EventQueue<WebSocketFrame> readFrames(int expectedFrameCount, int timeoutDuration, TimeUnit timeoutUnit) throws Exception
|
||||
{
|
||||
frameReader.frames.awaitEventCount(expectedFrameCount,timeoutDuration,timeoutUnit);
|
||||
return frameReader.frames;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.eclipse.jetty.websocket.common.test.IBlockheadClient#readResponseHeader()
|
||||
*/
|
||||
@Override
|
||||
public HttpResponse readResponseHeader() throws IOException
|
||||
{
|
||||
HttpResponse response = new HttpResponse();
|
||||
HttpResponseHeaderParser respParser = new HttpResponseHeaderParser(response);
|
||||
|
||||
byte buf[] = new byte[512];
|
||||
|
||||
while (!eof)
|
||||
{
|
||||
int available = in.available();
|
||||
int len = in.read(buf,0,Math.min(available,buf.length));
|
||||
if (len < 0)
|
||||
{
|
||||
eof = true;
|
||||
break;
|
||||
}
|
||||
else if (len > 0)
|
||||
{
|
||||
ByteBuffer bbuf = ByteBuffer.wrap(buf,0,len);
|
||||
if (LOG.isDebugEnabled())
|
||||
{
|
||||
LOG.debug("Read {} bytes: {}",len,BufferUtil.toDetailString(bbuf));
|
||||
}
|
||||
if (respParser.parse(bbuf) != null)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
remainingBuffer = response.getRemainingBuffer();
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.eclipse.jetty.websocket.common.test.IBlockheadClient#sendStandardRequest()
|
||||
*/
|
||||
@Override
|
||||
public void sendStandardRequest() throws IOException
|
||||
{
|
||||
StringBuilder req = generateUpgradeRequest();
|
||||
writeRaw(req.toString());
|
||||
}
|
||||
|
||||
public StringBuilder generateUpgradeRequest()
|
||||
{
|
||||
StringBuilder req = new StringBuilder();
|
||||
req.append("GET ").append(getRequestPath()).append(" HTTP/1.1\r\n");
|
||||
req.append("Host: ").append(getRequestHost()).append("\r\n");
|
||||
req.append("Upgrade: websocket\r\n");
|
||||
req.append("User-Agent: BlockheadClient/JettyTests\r\n");
|
||||
req.append("Connection: ").append(connectionValue).append("\r\n");
|
||||
for (String header : headers)
|
||||
{
|
||||
req.append(header);
|
||||
}
|
||||
req.append("Sec-WebSocket-Key: ").append(getRequestWebSocketKey()).append("\r\n");
|
||||
req.append("Sec-WebSocket-Origin: ").append(getRequestWebSocketOrigin()).append("\r\n");
|
||||
if (StringUtil.isNotBlank(protocols))
|
||||
{
|
||||
req.append("Sec-WebSocket-Protocol: ").append(protocols).append("\r\n");
|
||||
}
|
||||
|
||||
for (String xtension : extensions)
|
||||
{
|
||||
req.append("Sec-WebSocket-Extensions: ").append(xtension).append("\r\n");
|
||||
}
|
||||
req.append("Sec-WebSocket-Version: ").append(version).append("\r\n");
|
||||
req.append("\r\n");
|
||||
return req;
|
||||
}
|
||||
|
||||
public void setConnectionValue(String connectionValue)
|
||||
{
|
||||
this.connectionValue = connectionValue;
|
||||
}
|
||||
|
||||
public void setExecutor(ExecutorService executor)
|
||||
{
|
||||
this.executor = executor;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.eclipse.jetty.websocket.common.test.IBlockheadClient#setProtocols(java.lang.String)
|
||||
*/
|
||||
@Override
|
||||
public void setProtocols(String protocols)
|
||||
{
|
||||
this.protocols = protocols;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.eclipse.jetty.websocket.common.test.IBlockheadClient#setTimeout(int, java.util.concurrent.TimeUnit)
|
||||
*/
|
||||
@Override
|
||||
public void setTimeout(int duration, TimeUnit unit)
|
||||
{
|
||||
this.timeout = (int)TimeUnit.MILLISECONDS.convert(duration,unit);
|
||||
}
|
||||
|
||||
public void setVersion(int version)
|
||||
{
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
public void skipTo(String string) throws IOException
|
||||
{
|
||||
int state = 0;
|
||||
|
||||
while (true)
|
||||
{
|
||||
int b = in.read();
|
||||
if (b < 0)
|
||||
{
|
||||
throw new EOFException();
|
||||
}
|
||||
|
||||
if (b == string.charAt(state))
|
||||
{
|
||||
state++;
|
||||
if (state == string.length())
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
state = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void sleep(TimeUnit unit, int duration) throws InterruptedException
|
||||
{
|
||||
LOG.info("Sleeping for {} {}",duration,unit);
|
||||
unit.sleep(duration);
|
||||
LOG.info("Waking up from sleep");
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.eclipse.jetty.websocket.common.test.IBlockheadClient#write(org.eclipse.jetty.websocket.common.WebSocketFrame)
|
||||
*/
|
||||
@Override
|
||||
public void write(WebSocketFrame frame) throws IOException
|
||||
{
|
||||
LOG.debug("write(Frame->{}) to {}",frame,outgoing);
|
||||
if (LOG.isDebugEnabled())
|
||||
{
|
||||
frame.setMask(new byte[] { 0x00, 0x00, 0x00, 0x00 });
|
||||
}
|
||||
else
|
||||
{
|
||||
frame.setMask(clientmask);
|
||||
}
|
||||
extensionStack.outgoingFrame(frame,null,BatchMode.OFF);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.eclipse.jetty.websocket.common.test.IBlockheadClient#writeRaw(java.nio.ByteBuffer)
|
||||
*/
|
||||
@Override
|
||||
public void writeRaw(ByteBuffer buf) throws IOException
|
||||
{
|
||||
LOG.debug("write(ByteBuffer) {}",BufferUtil.toDetailString(buf));
|
||||
BufferUtil.writeTo(buf,out);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.eclipse.jetty.websocket.common.test.IBlockheadClient#writeRaw(java.nio.ByteBuffer, int)
|
||||
*/
|
||||
@Override
|
||||
public void writeRaw(ByteBuffer buf, int numBytes) throws IOException
|
||||
{
|
||||
int len = Math.min(numBytes,buf.remaining());
|
||||
byte arr[] = new byte[len];
|
||||
buf.get(arr,0,len);
|
||||
out.write(arr);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.eclipse.jetty.websocket.common.test.IBlockheadClient#writeRaw(java.lang.String)
|
||||
*/
|
||||
@Override
|
||||
public void writeRaw(String str) throws IOException
|
||||
{
|
||||
LOG.debug("write((String)[{}]){}{})",str.length(),'\n',str);
|
||||
out.write(str.getBytes(StandardCharsets.ISO_8859_1));
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.eclipse.jetty.websocket.common.test.IBlockheadClient#writeRawSlowly(java.nio.ByteBuffer, int)
|
||||
*/
|
||||
@Override
|
||||
public void writeRawSlowly(ByteBuffer buf, int segmentSize) throws IOException
|
||||
{
|
||||
while (buf.remaining() > 0)
|
||||
{
|
||||
writeRaw(buf,segmentSize);
|
||||
flush();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,325 +0,0 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// All rights reserved. This program and the accompanying materials
|
||||
// are made available under the terms of the Eclipse Public License v1.0
|
||||
// and Apache License v2.0 which accompanies this distribution.
|
||||
//
|
||||
// The Eclipse Public License is available at
|
||||
// http://www.eclipse.org/legal/epl-v10.html
|
||||
//
|
||||
// The Apache License v2.0 is available at
|
||||
// http://www.opensource.org/licenses/apache2.0.php
|
||||
//
|
||||
// You may elect to redistribute this code under either of these licenses.
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.websocket.common.test;
|
||||
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.SocketException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.eclipse.jetty.toolchain.test.EventQueue;
|
||||
import org.eclipse.jetty.util.BufferUtil;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
import org.eclipse.jetty.websocket.api.WebSocketPolicy;
|
||||
import org.eclipse.jetty.websocket.api.extensions.Frame;
|
||||
import org.eclipse.jetty.websocket.common.CloseInfo;
|
||||
import org.eclipse.jetty.websocket.common.Generator;
|
||||
import org.eclipse.jetty.websocket.common.OpCode;
|
||||
import org.eclipse.jetty.websocket.common.WebSocketFrame;
|
||||
import org.junit.Assert;
|
||||
|
||||
/**
|
||||
* Fuzzing utility for the AB tests.
|
||||
*/
|
||||
public class XFuzzer implements AutoCloseable
|
||||
{
|
||||
public static enum CloseState
|
||||
{
|
||||
OPEN,
|
||||
REMOTE_INITIATED,
|
||||
LOCAL_INITIATED
|
||||
}
|
||||
|
||||
public static enum SendMode
|
||||
{
|
||||
BULK,
|
||||
PER_FRAME,
|
||||
SLOW
|
||||
}
|
||||
|
||||
public static enum DisconnectMode
|
||||
{
|
||||
/** Disconnect occurred after a proper close handshake */
|
||||
CLEAN,
|
||||
/** Disconnect occurred in a harsh manner, without a close handshake */
|
||||
UNCLEAN
|
||||
}
|
||||
|
||||
private static final int KBYTE = 1024;
|
||||
private static final int MBYTE = KBYTE * KBYTE;
|
||||
|
||||
private static final Logger LOG = Log.getLogger(XFuzzer.class);
|
||||
|
||||
// Client side framing mask
|
||||
protected static final byte[] MASK =
|
||||
{ 0x11, 0x22, 0x33, 0x44 };
|
||||
|
||||
private final XBlockheadClient client;
|
||||
private final Generator generator;
|
||||
private final String testname;
|
||||
private SendMode sendMode = SendMode.BULK;
|
||||
private int slowSendSegmentSize = 5;
|
||||
|
||||
public XFuzzer(Fuzzed testcase) throws Exception
|
||||
{
|
||||
WebSocketPolicy policy = WebSocketPolicy.newClientPolicy();
|
||||
|
||||
int bigMessageSize = 20 * MBYTE;
|
||||
|
||||
policy.setMaxTextMessageSize(bigMessageSize);
|
||||
policy.setMaxBinaryMessageSize(bigMessageSize);
|
||||
policy.setIdleTimeout(5000);
|
||||
|
||||
this.client = new XBlockheadClient(policy,testcase.getServerURI());
|
||||
this.client.setTimeout(2,TimeUnit.SECONDS);
|
||||
this.generator = testcase.getLaxGenerator();
|
||||
this.testname = testcase.getTestMethodName();
|
||||
}
|
||||
|
||||
public ByteBuffer asNetworkBuffer(List<WebSocketFrame> send)
|
||||
{
|
||||
int buflen = 0;
|
||||
for (Frame f : send)
|
||||
{
|
||||
buflen += f.getPayloadLength() + Generator.MAX_HEADER_LENGTH;
|
||||
}
|
||||
ByteBuffer buf = ByteBuffer.allocate(buflen);
|
||||
|
||||
// Generate frames
|
||||
for (WebSocketFrame f : send)
|
||||
{
|
||||
setClientMask(f);
|
||||
generator.generateWholeFrame(f,buf);
|
||||
}
|
||||
buf.flip();
|
||||
return buf;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws Exception
|
||||
{
|
||||
this.client.disconnect();
|
||||
}
|
||||
|
||||
public void disconnect()
|
||||
{
|
||||
this.client.disconnect();
|
||||
}
|
||||
|
||||
public void connect() throws IOException
|
||||
{
|
||||
if (!client.isConnected())
|
||||
{
|
||||
client.connect();
|
||||
client.addHeader("X-TestCase: " + testname + "\r\n");
|
||||
client.sendStandardRequest();
|
||||
client.expectUpgradeResponse();
|
||||
}
|
||||
}
|
||||
|
||||
public void expect(List<WebSocketFrame> expect) throws Exception
|
||||
{
|
||||
expect(expect,10,TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
public void expect(List<WebSocketFrame> expect, int duration, TimeUnit unit) throws Exception
|
||||
{
|
||||
int expectedCount = expect.size();
|
||||
LOG.debug("expect() {} frame(s)",expect.size());
|
||||
|
||||
// Read frames
|
||||
EventQueue<WebSocketFrame> frames = client.readFrames(expect.size(),duration,unit);
|
||||
|
||||
String prefix = "";
|
||||
for (int i = 0; i < expectedCount; i++)
|
||||
{
|
||||
WebSocketFrame expected = expect.get(i);
|
||||
WebSocketFrame actual = frames.poll();
|
||||
|
||||
prefix = "Frame[" + i + "]";
|
||||
|
||||
LOG.debug("{} {}",prefix,actual);
|
||||
|
||||
Assert.assertThat(prefix + ".opcode",OpCode.name(actual.getOpCode()),is(OpCode.name(expected.getOpCode())));
|
||||
prefix += "/" + actual.getOpCode();
|
||||
if (expected.getOpCode() == OpCode.CLOSE)
|
||||
{
|
||||
CloseInfo expectedClose = new CloseInfo(expected);
|
||||
CloseInfo actualClose = new CloseInfo(actual);
|
||||
Assert.assertThat(prefix + ".statusCode",actualClose.getStatusCode(),is(expectedClose.getStatusCode()));
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert.assertThat(prefix + ".payloadLength",actual.getPayloadLength(),is(expected.getPayloadLength()));
|
||||
ByteBufferAssert.assertEquals(prefix + ".payload",expected.getPayload(),actual.getPayload());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void expect(WebSocketFrame expect) throws Exception
|
||||
{
|
||||
expect(Collections.singletonList(expect));
|
||||
}
|
||||
|
||||
public void expectNoMoreFrames()
|
||||
{
|
||||
// TODO Should test for no more frames. success if connection closed.
|
||||
}
|
||||
|
||||
public SendMode getSendMode()
|
||||
{
|
||||
return sendMode;
|
||||
}
|
||||
|
||||
public int getSlowSendSegmentSize()
|
||||
{
|
||||
return slowSendSegmentSize;
|
||||
}
|
||||
|
||||
public void send(ByteBuffer buf) throws IOException
|
||||
{
|
||||
Assert.assertThat("Client connected",client.isConnected(),is(true));
|
||||
LOG.debug("Sending bytes {}",BufferUtil.toDetailString(buf));
|
||||
if (sendMode == SendMode.SLOW)
|
||||
{
|
||||
client.writeRawSlowly(buf,slowSendSegmentSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
client.writeRaw(buf);
|
||||
}
|
||||
}
|
||||
|
||||
public void send(ByteBuffer buf, int numBytes) throws IOException
|
||||
{
|
||||
client.writeRaw(buf,numBytes);
|
||||
client.flush();
|
||||
}
|
||||
|
||||
public void send(List<WebSocketFrame> send) throws IOException
|
||||
{
|
||||
Assert.assertThat("Client connected",client.isConnected(),is(true));
|
||||
LOG.debug("[{}] Sending {} frames (mode {})",testname,send.size(),sendMode);
|
||||
if ((sendMode == SendMode.BULK) || (sendMode == SendMode.SLOW))
|
||||
{
|
||||
int buflen = 0;
|
||||
for (Frame f : send)
|
||||
{
|
||||
buflen += f.getPayloadLength() + Generator.MAX_HEADER_LENGTH;
|
||||
}
|
||||
ByteBuffer buf = ByteBuffer.allocate(buflen);
|
||||
|
||||
// Generate frames
|
||||
for (WebSocketFrame f : send)
|
||||
{
|
||||
setClientMask(f);
|
||||
buf.put(generator.generateHeaderBytes(f));
|
||||
if (f.hasPayload())
|
||||
{
|
||||
buf.put(f.getPayload());
|
||||
}
|
||||
}
|
||||
BufferUtil.flipToFlush(buf,0);
|
||||
|
||||
// Write Data Frame
|
||||
switch (sendMode)
|
||||
{
|
||||
case BULK:
|
||||
client.writeRaw(buf);
|
||||
break;
|
||||
case SLOW:
|
||||
client.writeRawSlowly(buf,slowSendSegmentSize);
|
||||
break;
|
||||
default:
|
||||
throw new RuntimeException("Whoops, unsupported sendMode: " + sendMode);
|
||||
}
|
||||
}
|
||||
else if (sendMode == SendMode.PER_FRAME)
|
||||
{
|
||||
for (WebSocketFrame f : send)
|
||||
{
|
||||
f.setMask(MASK); // make sure we have mask set
|
||||
// Using lax generator, generate and send
|
||||
ByteBuffer fullframe = ByteBuffer.allocate(f.getPayloadLength() + Generator.MAX_HEADER_LENGTH);
|
||||
BufferUtil.clearToFill(fullframe);
|
||||
generator.generateWholeFrame(f,fullframe);
|
||||
BufferUtil.flipToFlush(fullframe,0);
|
||||
client.writeRaw(fullframe);
|
||||
client.flush();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void send(WebSocketFrame send) throws IOException
|
||||
{
|
||||
send(Collections.singletonList(send));
|
||||
}
|
||||
|
||||
public void sendAndIgnoreBrokenPipe(List<WebSocketFrame> send) throws IOException
|
||||
{
|
||||
try
|
||||
{
|
||||
send(send);
|
||||
}
|
||||
catch (SocketException ignore)
|
||||
{
|
||||
// Potential for SocketException (Broken Pipe) here.
|
||||
// But not in 100% of testing scenarios. It is a safe
|
||||
// exception to ignore in this testing scenario, as the
|
||||
// slow writing of the frames can result in the server
|
||||
// throwing a PROTOCOL ERROR termination/close when it
|
||||
// encounters the bad continuation frame above (this
|
||||
// termination is the expected behavior), and this
|
||||
// early socket close can propagate back to the client
|
||||
// before it has a chance to finish writing out the
|
||||
// remaining frame octets
|
||||
Assert.assertThat("Allowed to be a broken pipe",ignore.getMessage().toLowerCase(Locale.ENGLISH),containsString("broken pipe"));
|
||||
}
|
||||
}
|
||||
|
||||
private void setClientMask(WebSocketFrame f)
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
{
|
||||
f.setMask(new byte[]
|
||||
{ 0x00, 0x00, 0x00, 0x00 });
|
||||
}
|
||||
else
|
||||
{
|
||||
f.setMask(MASK); // make sure we have mask set
|
||||
}
|
||||
}
|
||||
|
||||
public void setSendMode(SendMode sendMode)
|
||||
{
|
||||
this.sendMode = sendMode;
|
||||
}
|
||||
|
||||
public void setSlowSendSegmentSize(int segmentSize)
|
||||
{
|
||||
this.slowSendSegmentSize = segmentSize;
|
||||
}
|
||||
}
|
|
@ -1,147 +0,0 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// All rights reserved. This program and the accompanying materials
|
||||
// are made available under the terms of the Eclipse Public License v1.0
|
||||
// and Apache License v2.0 which accompanies this distribution.
|
||||
//
|
||||
// The Eclipse Public License is available at
|
||||
// http://www.eclipse.org/legal/epl-v10.html
|
||||
//
|
||||
// The Apache License v2.0 is available at
|
||||
// http://www.opensource.org/licenses/apache2.0.php
|
||||
//
|
||||
// You may elect to redistribute this code under either of these licenses.
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.websocket.server;
|
||||
|
||||
import static org.hamcrest.Matchers.is;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Arrays;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.server.ServerConnector;
|
||||
import org.eclipse.jetty.toolchain.test.EventQueue;
|
||||
import org.eclipse.jetty.toolchain.test.TestTracker;
|
||||
import org.eclipse.jetty.util.log.StacklessLogging;
|
||||
import org.eclipse.jetty.websocket.api.StatusCode;
|
||||
import org.eclipse.jetty.websocket.common.CloseInfo;
|
||||
import org.eclipse.jetty.websocket.common.OpCode;
|
||||
import org.eclipse.jetty.websocket.common.Parser;
|
||||
import org.eclipse.jetty.websocket.common.WebSocketFrame;
|
||||
import org.eclipse.jetty.websocket.common.frames.TextFrame;
|
||||
import org.eclipse.jetty.websocket.common.test.XBlockheadClient;
|
||||
import org.eclipse.jetty.websocket.server.examples.echo.BigEchoSocket;
|
||||
import org.eclipse.jetty.websocket.servlet.WebSocketServletFactory;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Assert;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
|
||||
public class AnnotatedMaxMessageSizeTest
|
||||
{
|
||||
@Rule
|
||||
public TestTracker tracker = new TestTracker();
|
||||
|
||||
private static Server server;
|
||||
private static ServerConnector connector;
|
||||
private static URI serverUri;
|
||||
|
||||
@BeforeClass
|
||||
public static void startServer() throws Exception
|
||||
{
|
||||
server = new Server();
|
||||
connector = new ServerConnector(server);
|
||||
server.addConnector(connector);
|
||||
|
||||
WebSocketHandler wsHandler = new WebSocketHandler()
|
||||
{
|
||||
@Override
|
||||
public void configure(WebSocketServletFactory factory)
|
||||
{
|
||||
factory.register(BigEchoSocket.class);
|
||||
}
|
||||
};
|
||||
|
||||
server.setHandler(wsHandler);
|
||||
server.start();
|
||||
|
||||
String host = connector.getHost();
|
||||
if (host == null)
|
||||
{
|
||||
host = "localhost";
|
||||
}
|
||||
int port = connector.getLocalPort();
|
||||
serverUri = new URI(String.format("ws://%s:%d/",host,port));
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void stopServer() throws Exception
|
||||
{
|
||||
server.stop();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEchoGood() throws IOException, Exception
|
||||
{
|
||||
XBlockheadClient client = new XBlockheadClient(serverUri);
|
||||
try
|
||||
{
|
||||
client.setProtocols("echo");
|
||||
client.connect();
|
||||
client.sendStandardRequest();
|
||||
client.expectUpgradeResponse();
|
||||
|
||||
// Generate text frame
|
||||
String msg = "this is an echo ... cho ... ho ... o";
|
||||
client.write(new TextFrame().setPayload(msg));
|
||||
|
||||
// Read frame (hopefully text frame)
|
||||
EventQueue<WebSocketFrame> frames = client.readFrames(1,30,TimeUnit.SECONDS);
|
||||
WebSocketFrame tf = frames.poll();
|
||||
Assert.assertThat("Text Frame.status code",tf.getPayloadAsUTF8(),is(msg));
|
||||
}
|
||||
finally
|
||||
{
|
||||
client.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Test(timeout=8000)
|
||||
public void testEchoTooBig() throws IOException, Exception
|
||||
{
|
||||
XBlockheadClient client = new XBlockheadClient(serverUri);
|
||||
try(StacklessLogging ignored = new StacklessLogging(Parser.class))
|
||||
{
|
||||
client.setProtocols("echo");
|
||||
client.connect();
|
||||
client.sendStandardRequest();
|
||||
client.expectUpgradeResponse();
|
||||
|
||||
// Generate text frame
|
||||
int size = 120 * 1024;
|
||||
byte buf[] = new byte[size]; // buffer bigger than maxMessageSize
|
||||
Arrays.fill(buf,(byte)'x');
|
||||
client.write(new TextFrame().setPayload(ByteBuffer.wrap(buf)));
|
||||
|
||||
// Read frame (hopefully close frame saying its too large)
|
||||
EventQueue<WebSocketFrame> frames = client.readFrames(1,30,TimeUnit.SECONDS);
|
||||
WebSocketFrame tf = frames.poll();
|
||||
Assert.assertThat("Frame is close", tf.getOpCode(), is(OpCode.CLOSE));
|
||||
CloseInfo close = new CloseInfo(tf);
|
||||
Assert.assertThat("Close Code", close.getStatusCode(), is(StatusCode.MESSAGE_TOO_LARGE));
|
||||
}
|
||||
finally
|
||||
{
|
||||
client.close();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,85 +0,0 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// All rights reserved. This program and the accompanying materials
|
||||
// are made available under the terms of the Eclipse Public License v1.0
|
||||
// and Apache License v2.0 which accompanies this distribution.
|
||||
//
|
||||
// The Eclipse Public License is available at
|
||||
// http://www.eclipse.org/legal/epl-v10.html
|
||||
//
|
||||
// The Apache License v2.0 is available at
|
||||
// http://www.opensource.org/licenses/apache2.0.php
|
||||
//
|
||||
// You may elect to redistribute this code under either of these licenses.
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.websocket.server;
|
||||
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.eclipse.jetty.toolchain.test.EventQueue;
|
||||
import org.eclipse.jetty.websocket.common.WebSocketFrame;
|
||||
import org.eclipse.jetty.websocket.common.frames.TextFrame;
|
||||
import org.eclipse.jetty.websocket.common.test.XBlockheadClient;
|
||||
import org.eclipse.jetty.websocket.common.test.HttpResponse;
|
||||
import org.eclipse.jetty.websocket.server.examples.MyEchoServlet;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Assume;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
public class ChromeTest
|
||||
{
|
||||
private static SimpleServletServer server;
|
||||
|
||||
@BeforeClass
|
||||
public static void startServer() throws Exception
|
||||
{
|
||||
server = new SimpleServletServer(new MyEchoServlet());
|
||||
server.start();
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void stopServer()
|
||||
{
|
||||
server.stop();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpgradeWithWebkitDeflateExtension() throws Exception
|
||||
{
|
||||
Assume.assumeTrue("Server has x-webkit-deflate-frame registered",
|
||||
server.getWebSocketServletFactory().getExtensionFactory().isAvailable("x-webkit-deflate-frame"));
|
||||
|
||||
XBlockheadClient client = new XBlockheadClient(server.getServerUri());
|
||||
try
|
||||
{
|
||||
client.addExtensions("x-webkit-deflate-frame");
|
||||
client.setProtocols("chat");
|
||||
client.connect();
|
||||
client.sendStandardRequest();
|
||||
HttpResponse response = client.expectUpgradeResponse();
|
||||
Assert.assertThat("Response",response.getExtensionsHeader(),containsString("x-webkit-deflate-frame"));
|
||||
|
||||
// Generate text frame
|
||||
String msg = "this is an echo ... cho ... ho ... o";
|
||||
client.write(new TextFrame().setPayload(msg));
|
||||
|
||||
// Read frame (hopefully text frame)
|
||||
EventQueue<WebSocketFrame> frames = client.readFrames(1,30,TimeUnit.SECONDS);
|
||||
WebSocketFrame tf = frames.poll();
|
||||
Assert.assertThat("Text Frame.status code",tf.getPayloadAsUTF8(),is(msg));
|
||||
}
|
||||
finally
|
||||
{
|
||||
client.close();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,74 +0,0 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// All rights reserved. This program and the accompanying materials
|
||||
// are made available under the terms of the Eclipse Public License v1.0
|
||||
// and Apache License v2.0 which accompanies this distribution.
|
||||
//
|
||||
// The Eclipse Public License is available at
|
||||
// http://www.eclipse.org/legal/epl-v10.html
|
||||
//
|
||||
// The Apache License v2.0 is available at
|
||||
// http://www.opensource.org/licenses/apache2.0.php
|
||||
//
|
||||
// You may elect to redistribute this code under either of these licenses.
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.websocket.server;
|
||||
|
||||
import static org.hamcrest.Matchers.*;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.eclipse.jetty.toolchain.test.EventQueue;
|
||||
import org.eclipse.jetty.websocket.common.WebSocketFrame;
|
||||
import org.eclipse.jetty.websocket.common.frames.TextFrame;
|
||||
import org.eclipse.jetty.websocket.common.test.XBlockheadClient;
|
||||
import org.eclipse.jetty.websocket.common.test.IBlockheadClient;
|
||||
import org.eclipse.jetty.websocket.server.examples.MyEchoServlet;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Assert;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
public class FirefoxTest
|
||||
{
|
||||
private static SimpleServletServer server;
|
||||
|
||||
@BeforeClass
|
||||
public static void startServer() throws Exception
|
||||
{
|
||||
server = new SimpleServletServer(new MyEchoServlet());
|
||||
server.start();
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void stopServer()
|
||||
{
|
||||
server.stop();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConnectionKeepAlive() throws Exception
|
||||
{
|
||||
try (IBlockheadClient client = new XBlockheadClient(server.getServerUri()))
|
||||
{
|
||||
// Odd Connection Header value seen in Firefox
|
||||
client.setConnectionValue("keep-alive, Upgrade");
|
||||
client.connect();
|
||||
client.sendStandardRequest();
|
||||
client.expectUpgradeResponse();
|
||||
|
||||
// Generate text frame
|
||||
String msg = "this is an echo ... cho ... ho ... o";
|
||||
client.write(new TextFrame().setPayload(msg));
|
||||
|
||||
// Read frame (hopefully text frame)
|
||||
EventQueue<WebSocketFrame> frames = client.readFrames(1, 30, TimeUnit.SECONDS);
|
||||
WebSocketFrame tf = frames.poll();
|
||||
Assert.assertThat("Text Frame.status code", tf.getPayloadAsUTF8(), is(msg));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,104 +0,0 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// All rights reserved. This program and the accompanying materials
|
||||
// are made available under the terms of the Eclipse Public License v1.0
|
||||
// and Apache License v2.0 which accompanies this distribution.
|
||||
//
|
||||
// The Eclipse Public License is available at
|
||||
// http://www.eclipse.org/legal/epl-v10.html
|
||||
//
|
||||
// The Apache License v2.0 is available at
|
||||
// http://www.opensource.org/licenses/apache2.0.php
|
||||
//
|
||||
// You may elect to redistribute this code under either of these licenses.
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.websocket.server;
|
||||
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.eclipse.jetty.toolchain.test.EventQueue;
|
||||
import org.eclipse.jetty.websocket.common.WebSocketFrame;
|
||||
import org.eclipse.jetty.websocket.common.frames.TextFrame;
|
||||
import org.eclipse.jetty.websocket.common.test.XBlockheadClient;
|
||||
import org.eclipse.jetty.websocket.common.test.HttpResponse;
|
||||
import org.eclipse.jetty.websocket.server.helper.EchoServlet;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Assert;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
public class FragmentExtensionTest
|
||||
{
|
||||
private static SimpleServletServer server;
|
||||
|
||||
@BeforeClass
|
||||
public static void startServer() throws Exception
|
||||
{
|
||||
server = new SimpleServletServer(new EchoServlet());
|
||||
server.start();
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void stopServer()
|
||||
{
|
||||
server.stop();
|
||||
}
|
||||
|
||||
private String[] split(String str, int partSize)
|
||||
{
|
||||
int strLength = str.length();
|
||||
int count = (int)Math.ceil((double)str.length() / partSize);
|
||||
String ret[] = new String[count];
|
||||
int idx;
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
idx = (i * partSize);
|
||||
ret[i] = str.substring(idx,Math.min(idx + partSize,strLength));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFragmentExtension() throws Exception
|
||||
{
|
||||
int fragSize = 4;
|
||||
|
||||
XBlockheadClient client = new XBlockheadClient(server.getServerUri());
|
||||
client.clearExtensions();
|
||||
client.addExtensions("fragment;maxLength=" + fragSize);
|
||||
client.setProtocols("onConnect");
|
||||
|
||||
try
|
||||
{
|
||||
// Make sure the read times out if there are problems with the implementation
|
||||
client.setTimeout(1,TimeUnit.SECONDS);
|
||||
client.connect();
|
||||
client.sendStandardRequest();
|
||||
HttpResponse resp = client.expectUpgradeResponse();
|
||||
|
||||
Assert.assertThat("Response",resp.getExtensionsHeader(),containsString("fragment"));
|
||||
|
||||
String msg = "Sent as a long message that should be split";
|
||||
client.write(new TextFrame().setPayload(msg));
|
||||
|
||||
String parts[] = split(msg,fragSize);
|
||||
EventQueue<WebSocketFrame> frames = client.readFrames(parts.length,1000,TimeUnit.MILLISECONDS);
|
||||
for (int i = 0; i < parts.length; i++)
|
||||
{
|
||||
WebSocketFrame frame = frames.poll();
|
||||
Assert.assertThat("text[" + i + "].payload",frame.getPayloadAsUTF8(),is(parts[i]));
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
client.close();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,84 +0,0 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// All rights reserved. This program and the accompanying materials
|
||||
// are made available under the terms of the Eclipse Public License v1.0
|
||||
// and Apache License v2.0 which accompanies this distribution.
|
||||
//
|
||||
// The Eclipse Public License is available at
|
||||
// http://www.eclipse.org/legal/epl-v10.html
|
||||
//
|
||||
// The Apache License v2.0 is available at
|
||||
// http://www.opensource.org/licenses/apache2.0.php
|
||||
//
|
||||
// You may elect to redistribute this code under either of these licenses.
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.websocket.server;
|
||||
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.eclipse.jetty.toolchain.test.EventQueue;
|
||||
import org.eclipse.jetty.websocket.common.WebSocketFrame;
|
||||
import org.eclipse.jetty.websocket.common.frames.TextFrame;
|
||||
import org.eclipse.jetty.websocket.common.test.XBlockheadClient;
|
||||
import org.eclipse.jetty.websocket.common.test.HttpResponse;
|
||||
import org.eclipse.jetty.websocket.server.helper.EchoServlet;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Assert;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
public class IdentityExtensionTest
|
||||
{
|
||||
private static SimpleServletServer server;
|
||||
|
||||
@BeforeClass
|
||||
public static void startServer() throws Exception
|
||||
{
|
||||
server = new SimpleServletServer(new EchoServlet());
|
||||
server.start();
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void stopServer()
|
||||
{
|
||||
server.stop();
|
||||
}
|
||||
|
||||
@Test(timeout = 10000)
|
||||
public void testIdentityExtension() throws Exception
|
||||
{
|
||||
XBlockheadClient client = new XBlockheadClient(server.getServerUri());
|
||||
client.clearExtensions();
|
||||
client.addExtensions("identity;param=0");
|
||||
client.addExtensions("identity;param=1, identity ; param = '2' ; other = ' some = value '");
|
||||
client.setProtocols("onConnect");
|
||||
|
||||
try
|
||||
{
|
||||
// Make sure the read times out if there are problems with the implementation
|
||||
client.setTimeout(1,TimeUnit.SECONDS);
|
||||
client.connect();
|
||||
client.sendStandardRequest();
|
||||
HttpResponse resp = client.expectUpgradeResponse();
|
||||
|
||||
Assert.assertThat("Response",resp.getExtensionsHeader(),containsString("identity"));
|
||||
|
||||
client.write(new TextFrame().setPayload("Hello"));
|
||||
|
||||
EventQueue<WebSocketFrame> frames = client.readFrames(1,1000,TimeUnit.MILLISECONDS);
|
||||
WebSocketFrame frame = frames.poll();
|
||||
Assert.assertThat("TEXT.payload",frame.getPayloadAsUTF8(),is("Hello"));
|
||||
}
|
||||
finally
|
||||
{
|
||||
client.close();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,115 +0,0 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// All rights reserved. This program and the accompanying materials
|
||||
// are made available under the terms of the Eclipse Public License v1.0
|
||||
// and Apache License v2.0 which accompanies this distribution.
|
||||
//
|
||||
// The Eclipse Public License is available at
|
||||
// http://www.eclipse.org/legal/epl-v10.html
|
||||
//
|
||||
// The Apache License v2.0 is available at
|
||||
// http://www.opensource.org/licenses/apache2.0.php
|
||||
//
|
||||
// You may elect to redistribute this code under either of these licenses.
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.websocket.server;
|
||||
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.eclipse.jetty.toolchain.test.EventQueue;
|
||||
import org.eclipse.jetty.websocket.api.StatusCode;
|
||||
import org.eclipse.jetty.websocket.api.annotations.WebSocket;
|
||||
import org.eclipse.jetty.websocket.common.CloseInfo;
|
||||
import org.eclipse.jetty.websocket.common.OpCode;
|
||||
import org.eclipse.jetty.websocket.common.WebSocketFrame;
|
||||
import org.eclipse.jetty.websocket.common.frames.TextFrame;
|
||||
import org.eclipse.jetty.websocket.common.test.XBlockheadClient;
|
||||
import org.eclipse.jetty.websocket.server.helper.RFCSocket;
|
||||
import org.eclipse.jetty.websocket.servlet.WebSocketServlet;
|
||||
import org.eclipse.jetty.websocket.servlet.WebSocketServletFactory;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Assert;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
public class IdleTimeoutTest
|
||||
{
|
||||
@WebSocket(maxIdleTime = 500)
|
||||
public static class FastTimeoutRFCSocket extends RFCSocket
|
||||
{
|
||||
}
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public static class TimeoutServlet extends WebSocketServlet
|
||||
{
|
||||
@Override
|
||||
public void configure(WebSocketServletFactory factory)
|
||||
{
|
||||
factory.register(FastTimeoutRFCSocket.class);
|
||||
}
|
||||
}
|
||||
|
||||
private static SimpleServletServer server;
|
||||
|
||||
@BeforeClass
|
||||
public static void startServer() throws Exception
|
||||
{
|
||||
server = new SimpleServletServer(new TimeoutServlet());
|
||||
server.start();
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void stopServer()
|
||||
{
|
||||
server.stop();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test IdleTimeout on server.
|
||||
* @throws Exception on test failure
|
||||
*/
|
||||
@Test
|
||||
public void testIdleTimeout() throws Exception
|
||||
{
|
||||
XBlockheadClient client = new XBlockheadClient(server.getServerUri());
|
||||
client.setProtocols("onConnect");
|
||||
client.setTimeout(2500,TimeUnit.MILLISECONDS);
|
||||
try
|
||||
{
|
||||
client.connect();
|
||||
client.sendStandardRequest();
|
||||
client.expectUpgradeResponse();
|
||||
|
||||
// This wait should be shorter than client timeout above, but
|
||||
// longer than server timeout configured in FastTimeoutRFCSocket
|
||||
// eg: websocket server endpoint timeout < this timeout < websocket client idle timeout
|
||||
client.sleep(TimeUnit.MILLISECONDS,1000);
|
||||
|
||||
// Write to server
|
||||
// This action is possible, but does nothing.
|
||||
// Server could be in a half-closed state at this point.
|
||||
// Where the server read is closed (due to timeout), but the server write is still open.
|
||||
// The server could not read this frame, if it is in this half closed state
|
||||
client.write(new TextFrame().setPayload("Hello"));
|
||||
|
||||
// Expect server to have closed due to its own timeout
|
||||
EventQueue<WebSocketFrame> frames = client.readFrames(1,30,TimeUnit.SECONDS);
|
||||
WebSocketFrame frame = frames.poll();
|
||||
Assert.assertThat("frame opcode",frame.getOpCode(),is(OpCode.CLOSE));
|
||||
CloseInfo close = new CloseInfo(frame);
|
||||
Assert.assertThat("close code",close.getStatusCode(),is(StatusCode.SHUTDOWN));
|
||||
Assert.assertThat("close reason",close.getReason(),containsString("Timeout"));
|
||||
}
|
||||
finally
|
||||
{
|
||||
client.close();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,137 +0,0 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// All rights reserved. This program and the accompanying materials
|
||||
// are made available under the terms of the Eclipse Public License v1.0
|
||||
// and Apache License v2.0 which accompanies this distribution.
|
||||
//
|
||||
// The Eclipse Public License is available at
|
||||
// http://www.eclipse.org/legal/epl-v10.html
|
||||
//
|
||||
// The Apache License v2.0 is available at
|
||||
// http://www.opensource.org/licenses/apache2.0.php
|
||||
//
|
||||
// You may elect to redistribute this code under either of these licenses.
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.websocket.server;
|
||||
|
||||
import static org.hamcrest.Matchers.anyOf;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.hamcrest.Matchers.notNullValue;
|
||||
|
||||
import java.net.HttpCookie;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.eclipse.jetty.websocket.api.UpgradeRequest;
|
||||
import org.eclipse.jetty.websocket.api.UpgradeResponse;
|
||||
import org.eclipse.jetty.websocket.common.test.XBlockheadClient;
|
||||
import org.eclipse.jetty.websocket.server.helper.EchoSocket;
|
||||
import org.eclipse.jetty.websocket.servlet.ServletUpgradeRequest;
|
||||
import org.eclipse.jetty.websocket.servlet.ServletUpgradeResponse;
|
||||
import org.eclipse.jetty.websocket.servlet.WebSocketCreator;
|
||||
import org.eclipse.jetty.websocket.servlet.WebSocketServlet;
|
||||
import org.eclipse.jetty.websocket.servlet.WebSocketServletFactory;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Assert;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
public class RequestHeadersTest
|
||||
{
|
||||
private static class EchoCreator implements WebSocketCreator
|
||||
{
|
||||
private UpgradeRequest lastRequest;
|
||||
private UpgradeResponse lastResponse;
|
||||
private EchoSocket echoSocket = new EchoSocket();
|
||||
|
||||
@Override
|
||||
public Object createWebSocket(ServletUpgradeRequest req, ServletUpgradeResponse resp)
|
||||
{
|
||||
this.lastRequest = req;
|
||||
this.lastResponse = resp;
|
||||
return echoSocket;
|
||||
}
|
||||
|
||||
public UpgradeRequest getLastRequest()
|
||||
{
|
||||
return lastRequest;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public UpgradeResponse getLastResponse()
|
||||
{
|
||||
return lastResponse;
|
||||
}
|
||||
}
|
||||
|
||||
public static class EchoRequestServlet extends WebSocketServlet
|
||||
{
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private static final long serialVersionUID = -6575001979901924179L;
|
||||
private final WebSocketCreator creator;
|
||||
|
||||
public EchoRequestServlet(WebSocketCreator creator)
|
||||
{
|
||||
this.creator = creator;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configure(WebSocketServletFactory factory)
|
||||
{
|
||||
factory.setCreator(this.creator);
|
||||
}
|
||||
}
|
||||
|
||||
private static SimpleServletServer server;
|
||||
private static EchoCreator echoCreator;
|
||||
|
||||
@BeforeClass
|
||||
public static void startServer() throws Exception
|
||||
{
|
||||
echoCreator = new EchoCreator();
|
||||
server = new SimpleServletServer(new EchoRequestServlet(echoCreator));
|
||||
server.start();
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void stopServer()
|
||||
{
|
||||
server.stop();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAccessRequestCookies() throws Exception
|
||||
{
|
||||
XBlockheadClient client = new XBlockheadClient(server.getServerUri());
|
||||
client.setTimeout(1,TimeUnit.SECONDS);
|
||||
|
||||
try
|
||||
{
|
||||
client.connect();
|
||||
client.addHeader("Cookie: fruit=Pear; type=Anjou\r\n");
|
||||
client.sendStandardRequest();
|
||||
client.expectUpgradeResponse();
|
||||
|
||||
UpgradeRequest req = echoCreator.getLastRequest();
|
||||
Assert.assertThat("Last Request",req,notNullValue());
|
||||
List<HttpCookie> cookies = req.getCookies();
|
||||
Assert.assertThat("Request cookies",cookies,notNullValue());
|
||||
Assert.assertThat("Request cookies.size",cookies.size(),is(2));
|
||||
for (HttpCookie cookie : cookies)
|
||||
{
|
||||
Assert.assertThat("Cookie name",cookie.getName(),anyOf(is("fruit"),is("type")));
|
||||
Assert.assertThat("Cookie value",cookie.getValue(),anyOf(is("Pear"),is("Anjou")));
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
client.close();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,183 +0,0 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// All rights reserved. This program and the accompanying materials
|
||||
// are made available under the terms of the Eclipse Public License v1.0
|
||||
// and Apache License v2.0 which accompanies this distribution.
|
||||
//
|
||||
// The Eclipse Public License is available at
|
||||
// http://www.eclipse.org/legal/epl-v10.html
|
||||
//
|
||||
// The Apache License v2.0 is available at
|
||||
// http://www.opensource.org/licenses/apache2.0.php
|
||||
//
|
||||
// You may elect to redistribute this code under either of these licenses.
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.websocket.server;
|
||||
|
||||
import static org.hamcrest.Matchers.is;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Arrays;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.eclipse.jetty.io.LeakTrackingByteBufferPool;
|
||||
import org.eclipse.jetty.io.MappedByteBufferPool;
|
||||
import org.eclipse.jetty.toolchain.test.EventQueue;
|
||||
import org.eclipse.jetty.util.BufferUtil;
|
||||
import org.eclipse.jetty.websocket.api.WebSocketPolicy;
|
||||
import org.eclipse.jetty.websocket.common.Generator;
|
||||
import org.eclipse.jetty.websocket.common.WebSocketFrame;
|
||||
import org.eclipse.jetty.websocket.common.frames.TextFrame;
|
||||
import org.eclipse.jetty.websocket.common.test.XBlockheadClient;
|
||||
import org.eclipse.jetty.websocket.common.test.LeakTrackingBufferPoolRule;
|
||||
import org.eclipse.jetty.websocket.server.examples.MyEchoServlet;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Assert;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import org.junit.Ignore;
|
||||
|
||||
/**
|
||||
* Test simulating a client that talks too quickly.
|
||||
* <p>
|
||||
* There is a class of client that will send the GET+Upgrade Request along with a few websocket frames in a single
|
||||
* network packet. This test attempts to perform this behavior as close as possible.
|
||||
*/
|
||||
public class TooFastClientTest
|
||||
{
|
||||
private static SimpleServletServer server;
|
||||
|
||||
@BeforeClass
|
||||
public static void startServer() throws Exception
|
||||
{
|
||||
server = new SimpleServletServer(new MyEchoServlet());
|
||||
server.start();
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void stopServer()
|
||||
{
|
||||
server.stop();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore("RELEASE")
|
||||
public void testUpgradeWithSmallFrames() throws Exception
|
||||
{
|
||||
XBlockheadClient client = new XBlockheadClient(server.getServerUri());
|
||||
try
|
||||
{
|
||||
client.connect();
|
||||
|
||||
// Create ByteBuffer representing the initial opening network packet from the client
|
||||
ByteBuffer initialPacket = ByteBuffer.allocate(4096);
|
||||
BufferUtil.clearToFill(initialPacket);
|
||||
|
||||
// Add upgrade request to packet
|
||||
StringBuilder upgradeRequest = client.generateUpgradeRequest();
|
||||
ByteBuffer upgradeBuffer = BufferUtil.toBuffer(upgradeRequest.toString(),StandardCharsets.UTF_8);
|
||||
initialPacket.put(upgradeBuffer);
|
||||
|
||||
// Add text frames
|
||||
Generator generator = new Generator(WebSocketPolicy.newClientPolicy(),
|
||||
new LeakTrackingBufferPoolRule("Generator"));
|
||||
|
||||
String msg1 = "Echo 1";
|
||||
String msg2 = "This is also an echooooo!";
|
||||
|
||||
TextFrame frame1 = new TextFrame().setPayload(msg1);
|
||||
TextFrame frame2 = new TextFrame().setPayload(msg2);
|
||||
|
||||
// Need to set frame mask (as these are client frames)
|
||||
byte mask[] = new byte[] { 0x11, 0x22, 0x33, 0x44 };
|
||||
frame1.setMask(mask);
|
||||
frame2.setMask(mask);
|
||||
|
||||
generator.generateWholeFrame(frame1,initialPacket);
|
||||
generator.generateWholeFrame(frame2,initialPacket);
|
||||
|
||||
// Write packet to network
|
||||
BufferUtil.flipToFlush(initialPacket,0);
|
||||
client.writeRaw(initialPacket);
|
||||
|
||||
// Expect upgrade
|
||||
client.expectUpgradeResponse();
|
||||
|
||||
// Read frames (hopefully text frames)
|
||||
EventQueue<WebSocketFrame> frames = client.readFrames(2,1,TimeUnit.SECONDS);
|
||||
WebSocketFrame tf = frames.poll();
|
||||
Assert.assertThat("Text Frame/msg1",tf.getPayloadAsUTF8(),is(msg1));
|
||||
tf = frames.poll();
|
||||
Assert.assertThat("Text Frame/msg2",tf.getPayloadAsUTF8(),is(msg2));
|
||||
}
|
||||
finally
|
||||
{
|
||||
client.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test where were a client sends a HTTP Upgrade to websocket AND enough websocket frame(s)
|
||||
* to completely overfill the {@link org.eclipse.jetty.io.AbstractConnection#getInputBufferSize()}
|
||||
* to test a situation where the WebSocket connection opens with prefill that exceeds
|
||||
* the normal input buffer sizes.
|
||||
* @throws Exception on test failure
|
||||
*/
|
||||
@Test
|
||||
@Ignore("RELEASE")
|
||||
public void testUpgradeWithLargeFrame() throws Exception
|
||||
{
|
||||
XBlockheadClient client = new XBlockheadClient(server.getServerUri());
|
||||
try
|
||||
{
|
||||
client.connect();
|
||||
|
||||
// Create ByteBuffer representing the initial opening network packet from the client
|
||||
ByteBuffer initialPacket = ByteBuffer.allocate(100 * 1024);
|
||||
BufferUtil.clearToFill(initialPacket);
|
||||
|
||||
// Add upgrade request to packet
|
||||
StringBuilder upgradeRequest = client.generateUpgradeRequest();
|
||||
ByteBuffer upgradeBuffer = BufferUtil.toBuffer(upgradeRequest.toString(),StandardCharsets.UTF_8);
|
||||
initialPacket.put(upgradeBuffer);
|
||||
|
||||
// Add text frames
|
||||
Generator generator = new Generator(WebSocketPolicy.newClientPolicy(),
|
||||
new LeakTrackingByteBufferPool(new MappedByteBufferPool.Tagged()));
|
||||
|
||||
byte bigMsgBytes[] = new byte[64*1024];
|
||||
Arrays.fill(bigMsgBytes,(byte)'x');
|
||||
String bigMsg = new String(bigMsgBytes, StandardCharsets.UTF_8);
|
||||
|
||||
// Need to set frame mask (as these are client frames)
|
||||
byte mask[] = new byte[] { 0x11, 0x22, 0x33, 0x44 };
|
||||
TextFrame frame = new TextFrame().setPayload(bigMsg);
|
||||
frame.setMask(mask);
|
||||
generator.generateWholeFrame(frame,initialPacket);
|
||||
|
||||
// Write packet to network
|
||||
BufferUtil.flipToFlush(initialPacket,0);
|
||||
client.writeRaw(initialPacket);
|
||||
|
||||
// Expect upgrade
|
||||
client.expectUpgradeResponse();
|
||||
|
||||
// Read frames (hopefully text frames)
|
||||
EventQueue<WebSocketFrame> frames = client.readFrames(1,1,TimeUnit.SECONDS);
|
||||
|
||||
WebSocketFrame tf = frames.poll();
|
||||
Assert.assertThat("Text Frame/msg1",tf.getPayloadAsUTF8(),is(bigMsg));
|
||||
}
|
||||
finally
|
||||
{
|
||||
client.close();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -1,73 +0,0 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// All rights reserved. This program and the accompanying materials
|
||||
// are made available under the terms of the Eclipse Public License v1.0
|
||||
// and Apache License v2.0 which accompanies this distribution.
|
||||
//
|
||||
// The Eclipse Public License is available at
|
||||
// http://www.eclipse.org/legal/epl-v10.html
|
||||
//
|
||||
// The Apache License v2.0 is available at
|
||||
// http://www.opensource.org/licenses/apache2.0.php
|
||||
//
|
||||
// You may elect to redistribute this code under either of these licenses.
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.websocket.server;
|
||||
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
|
||||
import org.eclipse.jetty.websocket.common.test.XBlockheadClient;
|
||||
import org.eclipse.jetty.websocket.common.test.HttpResponse;
|
||||
import org.eclipse.jetty.websocket.server.examples.MyEchoServlet;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Assert;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
public class WebSocketInvalidVersionTest
|
||||
{
|
||||
private static SimpleServletServer server;
|
||||
|
||||
@BeforeClass
|
||||
public static void startServer() throws Exception
|
||||
{
|
||||
server = new SimpleServletServer(new MyEchoServlet());
|
||||
server.start();
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void stopServer()
|
||||
{
|
||||
server.stop();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the requirement of responding with an http 400 when using a Sec-WebSocket-Version that is unsupported.
|
||||
* @throws Exception on test failure
|
||||
*/
|
||||
@Test
|
||||
public void testRequestVersion29() throws Exception
|
||||
{
|
||||
@SuppressWarnings("resource")
|
||||
XBlockheadClient client = new XBlockheadClient(server.getServerUri());
|
||||
client.setVersion(29); // intentionally bad version
|
||||
try
|
||||
{
|
||||
client.connect();
|
||||
client.sendStandardRequest();
|
||||
HttpResponse response = client.readResponseHeader();
|
||||
Assert.assertThat("Response Status Code",response.getStatusCode(),is(400));
|
||||
Assert.assertThat("Response Status Reason",response.getStatusReason(),containsString("Unsupported websocket version specification"));
|
||||
Assert.assertThat("Response Versions",response.getHeader("Sec-WebSocket-Version"),is("13"));
|
||||
}
|
||||
finally
|
||||
{
|
||||
client.disconnect();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,104 +0,0 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// All rights reserved. This program and the accompanying materials
|
||||
// are made available under the terms of the Eclipse Public License v1.0
|
||||
// and Apache License v2.0 which accompanies this distribution.
|
||||
//
|
||||
// The Eclipse Public License is available at
|
||||
// http://www.eclipse.org/legal/epl-v10.html
|
||||
//
|
||||
// The Apache License v2.0 is available at
|
||||
// http://www.opensource.org/licenses/apache2.0.php
|
||||
//
|
||||
// You may elect to redistribute this code under either of these licenses.
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.websocket.server;
|
||||
|
||||
import static org.hamcrest.Matchers.is;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.eclipse.jetty.toolchain.test.AdvancedRunner;
|
||||
import org.eclipse.jetty.toolchain.test.EventQueue;
|
||||
import org.eclipse.jetty.websocket.common.WebSocketFrame;
|
||||
import org.eclipse.jetty.websocket.common.frames.TextFrame;
|
||||
import org.eclipse.jetty.websocket.common.test.XBlockheadClient;
|
||||
import org.eclipse.jetty.websocket.common.test.IBlockheadClient;
|
||||
import org.eclipse.jetty.websocket.server.helper.SessionServlet;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Assert;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
/**
|
||||
* Testing various aspects of the server side support for WebSocket {@link org.eclipse.jetty.websocket.api.Session}
|
||||
*/
|
||||
@RunWith(AdvancedRunner.class)
|
||||
public class WebSocketServerSessionTest
|
||||
{
|
||||
private static SimpleServletServer server;
|
||||
|
||||
@BeforeClass
|
||||
public static void startServer() throws Exception
|
||||
{
|
||||
server = new SimpleServletServer(new SessionServlet());
|
||||
server.start();
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void stopServer()
|
||||
{
|
||||
server.stop();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDisconnect() throws Exception
|
||||
{
|
||||
URI uri = server.getServerUri().resolve("/test/disconnect");
|
||||
try (IBlockheadClient client = new XBlockheadClient(uri))
|
||||
{
|
||||
client.connect();
|
||||
client.sendStandardRequest();
|
||||
client.expectUpgradeResponse();
|
||||
|
||||
client.write(new TextFrame().setPayload("harsh-disconnect"));
|
||||
|
||||
client.awaitDisconnect(1, TimeUnit.SECONDS);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpgradeRequestResponse() throws Exception
|
||||
{
|
||||
URI uri = server.getServerUri().resolve("/test?snack=cashews&amount=handful&brand=off");
|
||||
try (IBlockheadClient client = new XBlockheadClient(uri))
|
||||
{
|
||||
client.connect();
|
||||
client.sendStandardRequest();
|
||||
client.expectUpgradeResponse();
|
||||
|
||||
// Ask the server socket for specific parameter map info
|
||||
client.write(new TextFrame().setPayload("getParameterMap|snack"));
|
||||
client.write(new TextFrame().setPayload("getParameterMap|amount"));
|
||||
client.write(new TextFrame().setPayload("getParameterMap|brand"));
|
||||
client.write(new TextFrame().setPayload("getParameterMap|cost")); // intentionally invalid
|
||||
|
||||
// Read frame (hopefully text frame)
|
||||
EventQueue<WebSocketFrame> frames = client.readFrames(4,5,TimeUnit.SECONDS);
|
||||
WebSocketFrame tf = frames.poll();
|
||||
Assert.assertThat("Parameter Map[snack]", tf.getPayloadAsUTF8(), is("[cashews]"));
|
||||
tf = frames.poll();
|
||||
Assert.assertThat("Parameter Map[amount]", tf.getPayloadAsUTF8(), is("[handful]"));
|
||||
tf = frames.poll();
|
||||
Assert.assertThat("Parameter Map[brand]", tf.getPayloadAsUTF8(), is("[off]"));
|
||||
tf = frames.poll();
|
||||
Assert.assertThat("Parameter Map[cost]", tf.getPayloadAsUTF8(), is("<null>"));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,343 +0,0 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// All rights reserved. This program and the accompanying materials
|
||||
// are made available under the terms of the Eclipse Public License v1.0
|
||||
// and Apache License v2.0 which accompanies this distribution.
|
||||
//
|
||||
// The Eclipse Public License is available at
|
||||
// http://www.eclipse.org/legal/epl-v10.html
|
||||
//
|
||||
// The Apache License v2.0 is available at
|
||||
// http://www.opensource.org/licenses/apache2.0.php
|
||||
//
|
||||
// You may elect to redistribute this code under either of these licenses.
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.websocket.server;
|
||||
|
||||
import static org.hamcrest.Matchers.is;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Arrays;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.eclipse.jetty.toolchain.test.AdvancedRunner;
|
||||
import org.eclipse.jetty.toolchain.test.EventQueue;
|
||||
import org.eclipse.jetty.util.Utf8Appendable.NotUtf8Exception;
|
||||
import org.eclipse.jetty.util.Utf8StringBuilder;
|
||||
import org.eclipse.jetty.util.log.StacklessLogging;
|
||||
import org.eclipse.jetty.util.log.StdErrLog;
|
||||
import org.eclipse.jetty.websocket.api.StatusCode;
|
||||
import org.eclipse.jetty.websocket.api.extensions.Frame;
|
||||
import org.eclipse.jetty.websocket.common.CloseInfo;
|
||||
import org.eclipse.jetty.websocket.common.Generator;
|
||||
import org.eclipse.jetty.websocket.common.OpCode;
|
||||
import org.eclipse.jetty.websocket.common.Parser;
|
||||
import org.eclipse.jetty.websocket.common.WebSocketFrame;
|
||||
import org.eclipse.jetty.websocket.common.frames.BinaryFrame;
|
||||
import org.eclipse.jetty.websocket.common.frames.ContinuationFrame;
|
||||
import org.eclipse.jetty.websocket.common.frames.TextFrame;
|
||||
import org.eclipse.jetty.websocket.common.test.XBlockheadClient;
|
||||
import org.eclipse.jetty.websocket.common.test.UnitGenerator;
|
||||
import org.eclipse.jetty.websocket.common.util.Hex;
|
||||
import org.eclipse.jetty.websocket.server.helper.RFCServlet;
|
||||
import org.eclipse.jetty.websocket.server.helper.RFCSocket;
|
||||
import org.eclipse.jetty.websocket.servlet.WebSocketServlet;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Assert;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
/**
|
||||
* Test various <a href="http://tools.ietf.org/html/rfc6455">RFC 6455</a> specified requirements placed on {@link WebSocketServlet}
|
||||
*/
|
||||
@RunWith(AdvancedRunner.class)
|
||||
public class WebSocketServletRFCTest
|
||||
{
|
||||
private static Generator generator = new UnitGenerator();
|
||||
private static SimpleServletServer server;
|
||||
|
||||
@BeforeClass
|
||||
public static void startServer() throws Exception
|
||||
{
|
||||
server = new SimpleServletServer(new RFCServlet());
|
||||
server.start();
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void stopServer()
|
||||
{
|
||||
server.stop();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param clazz the class to enable
|
||||
* @param enabled true to enable the stack traces (or not)
|
||||
* @deprecated use {@link StacklessLogging} in a try-with-resources block instead
|
||||
*/
|
||||
@Deprecated
|
||||
private void enableStacks(Class<?> clazz, boolean enabled)
|
||||
{
|
||||
StdErrLog log = StdErrLog.getLogger(clazz);
|
||||
log.setHideStacks(!enabled);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that aggregation of binary frames into a single message occurs
|
||||
* @throws Exception on test failure
|
||||
*/
|
||||
@Test
|
||||
public void testBinaryAggregate() throws Exception
|
||||
{
|
||||
XBlockheadClient client = new XBlockheadClient(server.getServerUri());
|
||||
try
|
||||
{
|
||||
client.connect();
|
||||
client.sendStandardRequest();
|
||||
client.expectUpgradeResponse();
|
||||
|
||||
// Generate binary frames
|
||||
byte buf1[] = new byte[128];
|
||||
byte buf2[] = new byte[128];
|
||||
byte buf3[] = new byte[128];
|
||||
|
||||
Arrays.fill(buf1,(byte)0xAA);
|
||||
Arrays.fill(buf2,(byte)0xBB);
|
||||
Arrays.fill(buf3,(byte)0xCC);
|
||||
|
||||
WebSocketFrame bin;
|
||||
|
||||
bin = new BinaryFrame().setPayload(buf1).setFin(false);
|
||||
|
||||
client.write(bin); // write buf1 (fin=false)
|
||||
|
||||
bin = new ContinuationFrame().setPayload(buf2).setFin(false);
|
||||
|
||||
client.write(bin); // write buf2 (fin=false)
|
||||
|
||||
bin = new ContinuationFrame().setPayload(buf3).setFin(true);
|
||||
|
||||
client.write(bin); // write buf3 (fin=true)
|
||||
|
||||
// Read frame echo'd back (hopefully a single binary frame)
|
||||
EventQueue<WebSocketFrame> frames = client.readFrames(1,30,TimeUnit.SECONDS);
|
||||
Frame binmsg = frames.poll();
|
||||
int expectedSize = buf1.length + buf2.length + buf3.length;
|
||||
Assert.assertThat("BinaryFrame.payloadLength",binmsg.getPayloadLength(),is(expectedSize));
|
||||
|
||||
int aaCount = 0;
|
||||
int bbCount = 0;
|
||||
int ccCount = 0;
|
||||
|
||||
ByteBuffer echod = binmsg.getPayload();
|
||||
while (echod.remaining() >= 1)
|
||||
{
|
||||
byte b = echod.get();
|
||||
switch (b)
|
||||
{
|
||||
case (byte)0xAA:
|
||||
aaCount++;
|
||||
break;
|
||||
case (byte)0xBB:
|
||||
bbCount++;
|
||||
break;
|
||||
case (byte)0xCC:
|
||||
ccCount++;
|
||||
break;
|
||||
default:
|
||||
Assert.fail(String.format("Encountered invalid byte 0x%02X",(byte)(0xFF & b)));
|
||||
}
|
||||
}
|
||||
Assert.assertThat("Echoed data count for 0xAA",aaCount,is(buf1.length));
|
||||
Assert.assertThat("Echoed data count for 0xBB",bbCount,is(buf2.length));
|
||||
Assert.assertThat("Echoed data count for 0xCC",ccCount,is(buf3.length));
|
||||
}
|
||||
finally
|
||||
{
|
||||
client.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Test(expected = NotUtf8Exception.class)
|
||||
public void testDetectBadUTF8()
|
||||
{
|
||||
byte buf[] = new byte[]
|
||||
{ (byte)0xC2, (byte)0xC3 };
|
||||
|
||||
Utf8StringBuilder utf = new Utf8StringBuilder();
|
||||
utf.append(buf,0,buf.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the requirement of issuing socket and receiving echo response
|
||||
* @throws Exception on test failure
|
||||
*/
|
||||
@Test
|
||||
public void testEcho() throws Exception
|
||||
{
|
||||
XBlockheadClient client = new XBlockheadClient(server.getServerUri());
|
||||
try
|
||||
{
|
||||
client.connect();
|
||||
client.sendStandardRequest();
|
||||
client.expectUpgradeResponse();
|
||||
|
||||
// Generate text frame
|
||||
String msg = "this is an echo ... cho ... ho ... o";
|
||||
client.write(new TextFrame().setPayload(msg));
|
||||
|
||||
// Read frame (hopefully text frame)
|
||||
EventQueue<WebSocketFrame> frames = client.readFrames(1,30,TimeUnit.SECONDS);
|
||||
WebSocketFrame tf = frames.poll();
|
||||
Assert.assertThat("Text Frame.status code",tf.getPayloadAsUTF8(),is(msg));
|
||||
}
|
||||
finally
|
||||
{
|
||||
client.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the requirement of responding with server terminated close code 1011 when there is an unhandled (internal server error) being produced by the
|
||||
* WebSocket POJO.
|
||||
* @throws Exception on test failure
|
||||
*/
|
||||
@Test
|
||||
public void testInternalError() throws Exception
|
||||
{
|
||||
try (XBlockheadClient client = new XBlockheadClient(server.getServerUri());
|
||||
StacklessLogging stackless=new StacklessLogging(RFCSocket.class))
|
||||
{
|
||||
client.connect();
|
||||
client.sendStandardRequest();
|
||||
client.expectUpgradeResponse();
|
||||
|
||||
// Generate text frame
|
||||
client.write(new TextFrame().setPayload("CRASH"));
|
||||
|
||||
// Read frame (hopefully close frame)
|
||||
EventQueue<WebSocketFrame> frames = client.readFrames(1,30,TimeUnit.SECONDS);
|
||||
Frame cf = frames.poll();
|
||||
CloseInfo close = new CloseInfo(cf);
|
||||
Assert.assertThat("Close Frame.status code",close.getStatusCode(),is(StatusCode.SERVER_ERROR));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test http://tools.ietf.org/html/rfc6455#section-4.1 where server side upgrade handling is supposed to be case insensitive.
|
||||
* <p>
|
||||
* This test will simulate a client requesting upgrade with all lowercase headers.
|
||||
* @throws Exception on test failure
|
||||
*/
|
||||
@Test
|
||||
public void testLowercaseUpgrade() throws Exception
|
||||
{
|
||||
XBlockheadClient client = new XBlockheadClient(server.getServerUri());
|
||||
try
|
||||
{
|
||||
client.connect();
|
||||
|
||||
StringBuilder req = new StringBuilder();
|
||||
req.append("GET ").append(client.getRequestPath()).append(" HTTP/1.1\r\n");
|
||||
req.append("Host: ").append(client.getRequestHost()).append("\r\n");
|
||||
req.append("Upgrade: websocket\r\n");
|
||||
req.append("connection: upgrade\r\n");
|
||||
req.append("sec-websocket-key: ").append(client.getRequestWebSocketKey()).append("\r\n");
|
||||
req.append("sec-websocket-origin: ").append(client.getRequestWebSocketOrigin()).append("\r\n");
|
||||
req.append("sec-websocket-protocol: echo\r\n");
|
||||
req.append("sec-websocket-version: 13\r\n");
|
||||
req.append("\r\n");
|
||||
client.writeRaw(req.toString());
|
||||
|
||||
client.expectUpgradeResponse();
|
||||
|
||||
// Generate text frame
|
||||
String msg = "this is an echo ... cho ... ho ... o";
|
||||
client.write(new TextFrame().setPayload(msg));
|
||||
|
||||
// Read frame (hopefully text frame)
|
||||
EventQueue<WebSocketFrame> frames = client.readFrames(1,30,TimeUnit.SECONDS);
|
||||
WebSocketFrame tf = frames.poll();
|
||||
Assert.assertThat("Text Frame.status code",tf.getPayloadAsUTF8(),is(msg));
|
||||
}
|
||||
finally
|
||||
{
|
||||
client.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTextNotUTF8() throws Exception
|
||||
{
|
||||
try (StacklessLogging stackless=new StacklessLogging(Parser.class);
|
||||
XBlockheadClient client = new XBlockheadClient(server.getServerUri()))
|
||||
{
|
||||
client.setProtocols("other");
|
||||
client.connect();
|
||||
client.sendStandardRequest();
|
||||
client.expectUpgradeResponse();
|
||||
|
||||
byte buf[] = new byte[]
|
||||
{ (byte)0xC2, (byte)0xC3 };
|
||||
|
||||
WebSocketFrame txt = new TextFrame().setPayload(ByteBuffer.wrap(buf));
|
||||
txt.setMask(Hex.asByteArray("11223344"));
|
||||
ByteBuffer bbHeader = generator.generateHeaderBytes(txt);
|
||||
client.writeRaw(bbHeader);
|
||||
client.writeRaw(txt.getPayload());
|
||||
|
||||
EventQueue<WebSocketFrame> frames = client.readFrames(1,30,TimeUnit.SECONDS);
|
||||
WebSocketFrame frame = frames.poll();
|
||||
Assert.assertThat("frames[0].opcode",frame.getOpCode(),is(OpCode.CLOSE));
|
||||
CloseInfo close = new CloseInfo(frame);
|
||||
Assert.assertThat("Close Status Code",close.getStatusCode(),is(StatusCode.BAD_PAYLOAD));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test http://tools.ietf.org/html/rfc6455#section-4.1 where server side upgrade handling is supposed to be case insensitive.
|
||||
* <p>
|
||||
* This test will simulate a client requesting upgrade with all uppercase headers.
|
||||
* @throws Exception on test failure
|
||||
*/
|
||||
@Test
|
||||
public void testUppercaseUpgrade() throws Exception
|
||||
{
|
||||
XBlockheadClient client = new XBlockheadClient(server.getServerUri());
|
||||
try
|
||||
{
|
||||
client.connect();
|
||||
|
||||
StringBuilder req = new StringBuilder();
|
||||
req.append("GET ").append(client.getRequestPath()).append(" HTTP/1.1\r\n");
|
||||
req.append("HOST: ").append(client.getRequestHost()).append("\r\n");
|
||||
req.append("UPGRADE: WEBSOCKET\r\n");
|
||||
req.append("CONNECTION: UPGRADE\r\n");
|
||||
req.append("SEC-WEBSOCKET-KEY: ").append(client.getRequestWebSocketKey()).append("\r\n");
|
||||
req.append("SEC-WEBSOCKET-ORIGIN: ").append(client.getRequestWebSocketOrigin()).append("\r\n");
|
||||
req.append("SEC-WEBSOCKET-PROTOCOL: ECHO\r\n");
|
||||
req.append("SEC-WEBSOCKET-VERSION: 13\r\n");
|
||||
req.append("\r\n");
|
||||
client.writeRaw(req.toString());
|
||||
|
||||
client.expectUpgradeResponse();
|
||||
|
||||
// Generate text frame
|
||||
String msg = "this is an echo ... cho ... ho ... o";
|
||||
client.write(new TextFrame().setPayload(msg));
|
||||
|
||||
// Read frame (hopefully text frame)
|
||||
EventQueue<WebSocketFrame> frames = client.readFrames(1,30,TimeUnit.SECONDS);
|
||||
WebSocketFrame tf = frames.poll();
|
||||
Assert.assertThat("Text Frame.status code",tf.getPayloadAsUTF8(),is(msg));
|
||||
}
|
||||
finally
|
||||
{
|
||||
client.close();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,130 +0,0 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// All rights reserved. This program and the accompanying materials
|
||||
// are made available under the terms of the Eclipse Public License v1.0
|
||||
// and Apache License v2.0 which accompanies this distribution.
|
||||
//
|
||||
// The Eclipse Public License is available at
|
||||
// http://www.eclipse.org/legal/epl-v10.html
|
||||
//
|
||||
// The Apache License v2.0 is available at
|
||||
// http://www.opensource.org/licenses/apache2.0.php
|
||||
//
|
||||
// You may elect to redistribute this code under either of these licenses.
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.websocket.server.misbehaving;
|
||||
|
||||
import static org.hamcrest.Matchers.instanceOf;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.eclipse.jetty.toolchain.test.EventQueue;
|
||||
import org.eclipse.jetty.util.log.StacklessLogging;
|
||||
import org.eclipse.jetty.websocket.api.StatusCode;
|
||||
import org.eclipse.jetty.websocket.common.CloseInfo;
|
||||
import org.eclipse.jetty.websocket.common.OpCode;
|
||||
import org.eclipse.jetty.websocket.common.WebSocketFrame;
|
||||
import org.eclipse.jetty.websocket.common.test.XBlockheadClient;
|
||||
import org.eclipse.jetty.websocket.common.test.IBlockheadClient;
|
||||
import org.eclipse.jetty.websocket.server.SimpleServletServer;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* Testing badly behaving Socket class implementations to get the best
|
||||
* error messages and state out of the websocket implementation.
|
||||
*/
|
||||
public class MisbehavingClassTest
|
||||
{
|
||||
private static SimpleServletServer server;
|
||||
private static BadSocketsServlet badSocketsServlet;
|
||||
|
||||
@BeforeClass
|
||||
public static void startServer() throws Exception
|
||||
{
|
||||
badSocketsServlet = new BadSocketsServlet();
|
||||
server = new SimpleServletServer(badSocketsServlet);
|
||||
server.start();
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void stopServer()
|
||||
{
|
||||
server.stop();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testListenerRuntimeOnConnect() throws Exception
|
||||
{
|
||||
try (IBlockheadClient client = new XBlockheadClient(server.getServerUri());
|
||||
StacklessLogging ignored = new StacklessLogging(ListenerRuntimeOnConnectSocket.class))
|
||||
{
|
||||
client.setProtocols("listener-runtime-connect");
|
||||
client.setTimeout(1,TimeUnit.SECONDS);
|
||||
ListenerRuntimeOnConnectSocket socket = badSocketsServlet.listenerRuntimeConnect;
|
||||
socket.reset();
|
||||
|
||||
client.connect();
|
||||
client.sendStandardRequest();
|
||||
client.expectUpgradeResponse();
|
||||
|
||||
EventQueue<WebSocketFrame> frames = client.readFrames(1,1,TimeUnit.SECONDS);
|
||||
WebSocketFrame frame = frames.poll();
|
||||
assertThat("frames[0].opcode",frame.getOpCode(),is(OpCode.CLOSE));
|
||||
CloseInfo close = new CloseInfo(frame);
|
||||
assertThat("Close Status Code",close.getStatusCode(),is(StatusCode.SERVER_ERROR));
|
||||
|
||||
client.write(close.asFrame()); // respond with close
|
||||
|
||||
// ensure server socket got close event
|
||||
assertThat("Close Latch",socket.closeLatch.await(1,TimeUnit.SECONDS),is(true));
|
||||
assertThat("closeStatusCode",socket.closeStatusCode,is(StatusCode.SERVER_ERROR));
|
||||
|
||||
// Validate errors
|
||||
assertThat("socket.onErrors",socket.errors.size(),is(1));
|
||||
Throwable cause = socket.errors.pop();
|
||||
assertThat("Error type",cause,instanceOf(RuntimeException.class));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAnnotatedRuntimeOnConnect() throws Exception
|
||||
{
|
||||
try (IBlockheadClient client = new XBlockheadClient(server.getServerUri());
|
||||
StacklessLogging ignored = new StacklessLogging(AnnotatedRuntimeOnConnectSocket.class))
|
||||
{
|
||||
client.setProtocols("annotated-runtime-connect");
|
||||
client.setTimeout(1,TimeUnit.SECONDS);
|
||||
AnnotatedRuntimeOnConnectSocket socket = badSocketsServlet.annotatedRuntimeConnect;
|
||||
socket.reset();
|
||||
|
||||
client.connect();
|
||||
client.sendStandardRequest();
|
||||
client.expectUpgradeResponse();
|
||||
|
||||
EventQueue<WebSocketFrame> frames = client.readFrames(1,1,TimeUnit.SECONDS);
|
||||
WebSocketFrame frame = frames.poll();
|
||||
assertThat("frames[0].opcode",frame.getOpCode(),is(OpCode.CLOSE));
|
||||
CloseInfo close = new CloseInfo(frame);
|
||||
assertThat("Close Status Code",close.getStatusCode(),is(StatusCode.SERVER_ERROR));
|
||||
|
||||
client.write(close.asFrame()); // respond with close
|
||||
|
||||
// ensure server socket got close event
|
||||
assertThat("Close Latch",socket.closeLatch.await(1,TimeUnit.SECONDS),is(true));
|
||||
assertThat("closeStatusCode",socket.closeStatusCode,is(StatusCode.SERVER_ERROR));
|
||||
|
||||
// Validate errors
|
||||
assertThat("socket.onErrors",socket.errors.size(),is(1));
|
||||
Throwable cause = socket.errors.pop();
|
||||
assertThat("Error type",cause,instanceOf(RuntimeException.class));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -26,4 +26,4 @@ org.eclipse.jetty.websocket.server.helper.RFCSocket.LEVEL=OFF
|
|||
|
||||
### Hiding Stack Traces from various test cases
|
||||
org.eclipse.jetty.websocket.tests.server.ABSocket.STACKS=OFF
|
||||
org.eclipse.jetty.websocket.server.WebSocketCloseTest$FastFailSocket.STACKS=OFF
|
||||
org.eclipse.jetty.websocket.tests.server.WebSocketCloseTest$FastFailSocket.STACKS=OFF
|
|
@ -49,7 +49,7 @@ public abstract class AbstractTrackingEndpoint<T>
|
|||
LOG = Log.getLogger(this.getClass().getName() + "." + id);
|
||||
}
|
||||
|
||||
public void assertCloseInfo(String prefix, int expectedCloseStatusCode, Matcher<String> reasonMatcher) throws InterruptedException
|
||||
public void assertCloseInfo(String prefix, int expectedCloseStatusCode, Matcher<? super String> reasonMatcher) throws InterruptedException
|
||||
{
|
||||
CloseInfo close = closeInfo.get();
|
||||
assertThat(prefix + " close info", close, Matchers.notNullValue());
|
||||
|
|
|
@ -23,10 +23,12 @@ import java.net.URI;
|
|||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
import org.eclipse.jetty.client.HttpClient;
|
||||
import org.eclipse.jetty.util.B64Code;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
import org.eclipse.jetty.websocket.api.Session;
|
||||
|
@ -67,4 +69,16 @@ public class UntrustedWSClient extends WebSocketClient
|
|||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static String getStaticWebSocketKey()
|
||||
{
|
||||
return "dGhlIHNhbXBsZSBub25jZQ==";
|
||||
}
|
||||
|
||||
public static String genRandomWebSocketKey()
|
||||
{
|
||||
byte[] bytes = new byte[16];
|
||||
ThreadLocalRandom.current().nextBytes(bytes);
|
||||
return new String(B64Code.encode(bytes));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,231 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// All rights reserved. This program and the accompanying materials
|
||||
// are made available under the terms of the Eclipse Public License v1.0
|
||||
// and Apache License v2.0 which accompanies this distribution.
|
||||
//
|
||||
// The Eclipse Public License is available at
|
||||
// http://www.eclipse.org/legal/epl-v10.html
|
||||
//
|
||||
// The Apache License v2.0 is available at
|
||||
// http://www.opensource.org/licenses/apache2.0.php
|
||||
//
|
||||
// You may elect to redistribute this code under either of these licenses.
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.websocket.tests;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.hamcrest.Matchers.notNullValue;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.URL;
|
||||
|
||||
import org.eclipse.jetty.annotations.AnnotationConfiguration;
|
||||
import org.eclipse.jetty.plus.webapp.EnvConfiguration;
|
||||
import org.eclipse.jetty.plus.webapp.PlusConfiguration;
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.server.ServerConnector;
|
||||
import org.eclipse.jetty.server.handler.ContextHandlerCollection;
|
||||
import org.eclipse.jetty.server.handler.HandlerCollection;
|
||||
import org.eclipse.jetty.toolchain.test.FS;
|
||||
import org.eclipse.jetty.toolchain.test.IO;
|
||||
import org.eclipse.jetty.toolchain.test.JAR;
|
||||
import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
|
||||
import org.eclipse.jetty.toolchain.test.OS;
|
||||
import org.eclipse.jetty.toolchain.test.TestingDir;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
import org.eclipse.jetty.util.resource.Resource;
|
||||
import org.eclipse.jetty.webapp.Configuration;
|
||||
import org.eclipse.jetty.webapp.FragmentConfiguration;
|
||||
import org.eclipse.jetty.webapp.MetaInfConfiguration;
|
||||
import org.eclipse.jetty.webapp.WebAppContext;
|
||||
import org.eclipse.jetty.webapp.WebInfConfiguration;
|
||||
import org.eclipse.jetty.webapp.WebXmlConfiguration;
|
||||
|
||||
/**
|
||||
* Utility to build out exploded directory WebApps, in the /target/tests/ directory, for testing out servers that use javax.websocket endpoints.
|
||||
* <p>
|
||||
* This is particularly useful when the WebSocket endpoints are discovered via the javax.websocket annotation scanning.
|
||||
*/
|
||||
public class WSServer
|
||||
{
|
||||
private static final Logger LOG = Log.getLogger(WSServer.class);
|
||||
private final File contextDir;
|
||||
private final String contextPath;
|
||||
private Server server;
|
||||
private URI serverUri;
|
||||
private ContextHandlerCollection contexts;
|
||||
private File webinf;
|
||||
private File classesDir;
|
||||
|
||||
public WSServer(TestingDir testdir, String contextName)
|
||||
{
|
||||
this(testdir.getPath().toFile(),contextName);
|
||||
}
|
||||
|
||||
public WSServer(File testdir, String contextName)
|
||||
{
|
||||
this.contextDir = new File(testdir,contextName);
|
||||
this.contextPath = "/" + contextName;
|
||||
FS.ensureEmpty(contextDir);
|
||||
}
|
||||
|
||||
public void copyClass(Class<?> clazz) throws Exception
|
||||
{
|
||||
ClassLoader cl = Thread.currentThread().getContextClassLoader();
|
||||
String endpointPath = clazz.getName().replace('.','/') + ".class";
|
||||
URL classUrl = cl.getResource(endpointPath);
|
||||
assertThat("Class URL for: " + clazz,classUrl,notNullValue());
|
||||
File destFile = new File(classesDir,OS.separators(endpointPath));
|
||||
FS.ensureDirExists(destFile.getParentFile());
|
||||
File srcFile = new File(classUrl.toURI());
|
||||
IO.copy(srcFile,destFile);
|
||||
}
|
||||
|
||||
public void copyEndpoint(Class<?> endpointClass) throws Exception
|
||||
{
|
||||
copyClass(endpointClass);
|
||||
}
|
||||
|
||||
public void copyLib(Class<?> clazz, String jarFileName) throws URISyntaxException, IOException
|
||||
{
|
||||
webinf = new File(contextDir,"WEB-INF");
|
||||
FS.ensureDirExists(webinf);
|
||||
File libDir = new File(webinf,"lib");
|
||||
FS.ensureDirExists(libDir);
|
||||
File jarFile = new File(libDir, jarFileName);
|
||||
|
||||
URL codeSourceURL = clazz.getProtectionDomain().getCodeSource().getLocation();
|
||||
assertThat("Class CodeSource URL is file scheme", codeSourceURL.getProtocol(), is("file"));
|
||||
|
||||
File sourceCodeSourceFile = new File(codeSourceURL.toURI());
|
||||
if (sourceCodeSourceFile.isDirectory())
|
||||
{
|
||||
LOG.info("Creating " + jarFile + " from " + sourceCodeSourceFile);
|
||||
JAR.create(sourceCodeSourceFile, jarFile);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG.info("Copying " + sourceCodeSourceFile + " to " + jarFile);
|
||||
IO.copy(sourceCodeSourceFile, jarFile);
|
||||
}
|
||||
}
|
||||
|
||||
public void copyWebInf(String testResourceName) throws IOException
|
||||
{
|
||||
webinf = new File(contextDir,"WEB-INF");
|
||||
FS.ensureDirExists(webinf);
|
||||
classesDir = new File(webinf,"classes");
|
||||
FS.ensureDirExists(classesDir);
|
||||
File webxml = new File(webinf,"web.xml");
|
||||
File testWebXml = MavenTestingUtils.getTestResourceFile(testResourceName);
|
||||
IO.copy(testWebXml,webxml);
|
||||
}
|
||||
|
||||
public WebAppContext createWebAppContext() throws MalformedURLException, IOException
|
||||
{
|
||||
WebAppContext context = new WebAppContext();
|
||||
context.setContextPath(this.contextPath);
|
||||
context.setBaseResource(Resource.newResource(this.contextDir));
|
||||
context.setAttribute("org.eclipse.jetty.websocket.jsr356",Boolean.TRUE);
|
||||
|
||||
// @formatter:off
|
||||
context.setConfigurations(new Configuration[] {
|
||||
new AnnotationConfiguration(),
|
||||
new WebXmlConfiguration(),
|
||||
new WebInfConfiguration(),
|
||||
new PlusConfiguration(),
|
||||
new MetaInfConfiguration(),
|
||||
new FragmentConfiguration(),
|
||||
new EnvConfiguration()});
|
||||
// @formatter:on
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
public void createWebInf() throws IOException
|
||||
{
|
||||
copyWebInf("empty-web.xml");
|
||||
}
|
||||
|
||||
public void deployWebapp(WebAppContext webapp) throws Exception
|
||||
{
|
||||
contexts.addHandler(webapp);
|
||||
contexts.manage(webapp);
|
||||
webapp.start();
|
||||
if (LOG.isDebugEnabled())
|
||||
{
|
||||
webapp.dump(System.err);
|
||||
}
|
||||
}
|
||||
|
||||
public void dump()
|
||||
{
|
||||
server.dumpStdErr();
|
||||
}
|
||||
|
||||
public URI getServerBaseURI()
|
||||
{
|
||||
return serverUri;
|
||||
}
|
||||
|
||||
public Server getServer()
|
||||
{
|
||||
return server;
|
||||
}
|
||||
|
||||
public File getWebAppDir()
|
||||
{
|
||||
return this.contextDir;
|
||||
}
|
||||
|
||||
public void start() throws Exception
|
||||
{
|
||||
server = new Server();
|
||||
ServerConnector connector = new ServerConnector(server);
|
||||
connector.setPort(0);
|
||||
server.addConnector(connector);
|
||||
|
||||
HandlerCollection handlers = new HandlerCollection();
|
||||
contexts = new ContextHandlerCollection();
|
||||
handlers.addHandler(contexts);
|
||||
server.setHandler(handlers);
|
||||
|
||||
server.start();
|
||||
|
||||
String host = connector.getHost();
|
||||
if (host == null)
|
||||
{
|
||||
host = "localhost";
|
||||
}
|
||||
int port = connector.getLocalPort();
|
||||
serverUri = new URI(String.format("ws://%s:%d%s/",host,port,contextPath));
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Server started on {}",serverUri);
|
||||
}
|
||||
|
||||
public void stop()
|
||||
{
|
||||
if (server != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
server.stop();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
e.printStackTrace(System.err);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// All rights reserved. This program and the accompanying materials
|
||||
// are made available under the terms of the Eclipse Public License v1.0
|
||||
// and Apache License v2.0 which accompanies this distribution.
|
||||
//
|
||||
// The Eclipse Public License is available at
|
||||
// http://www.eclipse.org/legal/epl-v10.html
|
||||
//
|
||||
// The Apache License v2.0 is available at
|
||||
// http://www.opensource.org/licenses/apache2.0.php
|
||||
//
|
||||
// You may elect to redistribute this code under either of these licenses.
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.websocket.tests.servlets;
|
||||
|
||||
import org.eclipse.jetty.websocket.servlet.WebSocketServlet;
|
||||
import org.eclipse.jetty.websocket.servlet.WebSocketServletFactory;
|
||||
|
||||
/**
|
||||
* Example servlet for most basic form.
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class EchoServlet extends WebSocketServlet
|
||||
{
|
||||
@Override
|
||||
public void configure(WebSocketServletFactory factory)
|
||||
{
|
||||
factory.register(EchoSocket.class);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// All rights reserved. This program and the accompanying materials
|
||||
// are made available under the terms of the Eclipse Public License v1.0
|
||||
// and Apache License v2.0 which accompanies this distribution.
|
||||
//
|
||||
// The Eclipse Public License is available at
|
||||
// http://www.eclipse.org/legal/epl-v10.html
|
||||
//
|
||||
// The Apache License v2.0 is available at
|
||||
// http://www.opensource.org/licenses/apache2.0.php
|
||||
//
|
||||
// You may elect to redistribute this code under either of these licenses.
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.websocket.tests.servlets;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.eclipse.jetty.io.RuntimeIOException;
|
||||
import org.eclipse.jetty.websocket.api.BatchMode;
|
||||
import org.eclipse.jetty.websocket.api.RemoteEndpoint;
|
||||
import org.eclipse.jetty.websocket.api.WebSocketAdapter;
|
||||
|
||||
/**
|
||||
* Example of a basic blocking echo socket.
|
||||
*/
|
||||
public class EchoSocket extends WebSocketAdapter
|
||||
{
|
||||
@Override
|
||||
public void onWebSocketText(String message)
|
||||
{
|
||||
if (isNotConnected())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// echo the data back
|
||||
RemoteEndpoint remote = getRemote();
|
||||
remote.sendString(message);
|
||||
if (remote.getBatchMode() == BatchMode.ON)
|
||||
remote.flush();
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
throw new RuntimeIOException(e);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,189 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// All rights reserved. This program and the accompanying materials
|
||||
// are made available under the terms of the Eclipse Public License v1.0
|
||||
// and Apache License v2.0 which accompanies this distribution.
|
||||
//
|
||||
// The Eclipse Public License is available at
|
||||
// http://www.eclipse.org/legal/epl-v10.html
|
||||
//
|
||||
// The Apache License v2.0 is available at
|
||||
// http://www.opensource.org/licenses/apache2.0.php
|
||||
//
|
||||
// You may elect to redistribute this code under either of these licenses.
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.websocket.tests.server;
|
||||
|
||||
import static org.hamcrest.Matchers.anything;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Arrays;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.server.ServerConnector;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
import org.eclipse.jetty.websocket.api.BatchMode;
|
||||
import org.eclipse.jetty.websocket.api.RemoteEndpoint;
|
||||
import org.eclipse.jetty.websocket.api.Session;
|
||||
import org.eclipse.jetty.websocket.api.StatusCode;
|
||||
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage;
|
||||
import org.eclipse.jetty.websocket.api.annotations.WebSocket;
|
||||
import org.eclipse.jetty.websocket.api.util.WSURI;
|
||||
import org.eclipse.jetty.websocket.client.ClientUpgradeRequest;
|
||||
import org.eclipse.jetty.websocket.client.WebSocketClient;
|
||||
import org.eclipse.jetty.websocket.server.WebSocketHandler;
|
||||
import org.eclipse.jetty.websocket.servlet.WebSocketServletFactory;
|
||||
import org.eclipse.jetty.websocket.tests.Defaults;
|
||||
import org.eclipse.jetty.websocket.tests.TrackingEndpoint;
|
||||
import org.junit.After;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.TestName;
|
||||
|
||||
public class AnnotatedMaxMessageSizeTest
|
||||
{
|
||||
@WebSocket(maxTextMessageSize = 64 * 1024, maxBinaryMessageSize = 64 * 1024)
|
||||
public static class BigEchoSocket
|
||||
{
|
||||
private static final Logger LOG = Log.getLogger(BigEchoSocket.class);
|
||||
|
||||
@OnWebSocketMessage
|
||||
public void onBinary(Session session, byte buf[], int offset, int length) throws IOException
|
||||
{
|
||||
if (!session.isOpen())
|
||||
{
|
||||
LOG.warn("Session is closed");
|
||||
return;
|
||||
}
|
||||
RemoteEndpoint remote = session.getRemote();
|
||||
remote.sendBytes(ByteBuffer.wrap(buf, offset, length), null);
|
||||
if (remote.getBatchMode() == BatchMode.ON)
|
||||
remote.flush();
|
||||
}
|
||||
|
||||
@OnWebSocketMessage
|
||||
public void onText(Session session, String message) throws IOException
|
||||
{
|
||||
if (!session.isOpen())
|
||||
{
|
||||
LOG.warn("Session is closed");
|
||||
return;
|
||||
}
|
||||
RemoteEndpoint remote = session.getRemote();
|
||||
remote.sendString(message, null);
|
||||
if (remote.getBatchMode() == BatchMode.ON)
|
||||
remote.flush();
|
||||
}
|
||||
}
|
||||
|
||||
private static Server server;
|
||||
private static URI serverUri;
|
||||
|
||||
@BeforeClass
|
||||
public static void startServer() throws Exception
|
||||
{
|
||||
server = new Server();
|
||||
ServerConnector connector = new ServerConnector(server);
|
||||
server.addConnector(connector);
|
||||
|
||||
WebSocketHandler wsHandler = new WebSocketHandler()
|
||||
{
|
||||
@Override
|
||||
public void configure(WebSocketServletFactory factory)
|
||||
{
|
||||
factory.register(BigEchoSocket.class);
|
||||
}
|
||||
};
|
||||
|
||||
server.setHandler(wsHandler);
|
||||
server.start();
|
||||
|
||||
serverUri = WSURI.toWebsocket(server.getURI()).resolve("/");
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void stopServer() throws Exception
|
||||
{
|
||||
server.stop();
|
||||
}
|
||||
|
||||
@Rule
|
||||
public TestName testname = new TestName();
|
||||
|
||||
private WebSocketClient client;
|
||||
|
||||
@Before
|
||||
public void startClient() throws Exception
|
||||
{
|
||||
client = new WebSocketClient();
|
||||
client.start();
|
||||
}
|
||||
|
||||
@After
|
||||
public void stopClient() throws Exception
|
||||
{
|
||||
client.stop();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEchoGood() throws Exception
|
||||
{
|
||||
URI wsUri = serverUri.resolve("/");
|
||||
|
||||
TrackingEndpoint clientSocket = new TrackingEndpoint(testname.getMethodName());
|
||||
ClientUpgradeRequest upgradeRequest = new ClientUpgradeRequest();
|
||||
upgradeRequest.setSubProtocols("echo");
|
||||
Future<Session> clientConnectFuture = client.connect(clientSocket, wsUri, upgradeRequest);
|
||||
|
||||
Session clientSession = clientConnectFuture.get(Defaults.CONNECT_TIMEOUT_MS, TimeUnit.MILLISECONDS);
|
||||
|
||||
// Message
|
||||
String msg = "this is an echo ... cho ... ho ... o";
|
||||
clientSession.getRemote().sendString(msg);
|
||||
|
||||
// Read message
|
||||
String incomingMsg = clientSocket.messageQueue.poll(5, TimeUnit.SECONDS);
|
||||
Assert.assertThat("Incoming Message", incomingMsg, is(msg));
|
||||
|
||||
clientSession.close();
|
||||
}
|
||||
|
||||
@Test(timeout = 60000)
|
||||
public void testEchoTooBig() throws Exception
|
||||
{
|
||||
URI wsUri = serverUri.resolve("/");
|
||||
|
||||
TrackingEndpoint clientSocket = new TrackingEndpoint(testname.getMethodName());
|
||||
ClientUpgradeRequest upgradeRequest = new ClientUpgradeRequest();
|
||||
upgradeRequest.setSubProtocols("echo");
|
||||
Future<Session> clientConnectFuture = client.connect(clientSocket, wsUri, upgradeRequest);
|
||||
|
||||
Session clientSession = clientConnectFuture.get(Defaults.CONNECT_TIMEOUT_MS, TimeUnit.MILLISECONDS);
|
||||
|
||||
// Big Message
|
||||
int size = 120 * 1024;
|
||||
byte buf[] = new byte[size]; // buffer bigger than maxMessageSize
|
||||
Arrays.fill(buf, (byte) 'x');
|
||||
String msg = new String(buf, StandardCharsets.UTF_8);
|
||||
clientSession.getRemote().sendString(msg);
|
||||
|
||||
// Read message
|
||||
clientSocket.awaitCloseEvent("Client");
|
||||
clientSocket.assertCloseInfo("Client", StatusCode.MESSAGE_TOO_LARGE, anything());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,112 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// All rights reserved. This program and the accompanying materials
|
||||
// are made available under the terms of the Eclipse Public License v1.0
|
||||
// and Apache License v2.0 which accompanies this distribution.
|
||||
//
|
||||
// The Eclipse Public License is available at
|
||||
// http://www.eclipse.org/legal/epl-v10.html
|
||||
//
|
||||
// The Apache License v2.0 is available at
|
||||
// http://www.opensource.org/licenses/apache2.0.php
|
||||
//
|
||||
// You may elect to redistribute this code under either of these licenses.
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.websocket.tests.server;
|
||||
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.eclipse.jetty.websocket.api.Session;
|
||||
import org.eclipse.jetty.websocket.api.extensions.ExtensionConfig;
|
||||
import org.eclipse.jetty.websocket.client.ClientUpgradeRequest;
|
||||
import org.eclipse.jetty.websocket.client.WebSocketClient;
|
||||
import org.eclipse.jetty.websocket.tests.Defaults;
|
||||
import org.eclipse.jetty.websocket.tests.SimpleServletServer;
|
||||
import org.eclipse.jetty.websocket.tests.TrackingEndpoint;
|
||||
import org.eclipse.jetty.websocket.tests.servlets.EchoServlet;
|
||||
import org.junit.After;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Assume;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.TestName;
|
||||
|
||||
public class ChromeTest
|
||||
{
|
||||
private static SimpleServletServer server;
|
||||
|
||||
@BeforeClass
|
||||
public static void startServer() throws Exception
|
||||
{
|
||||
server = new SimpleServletServer(new EchoServlet());
|
||||
server.start();
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void stopServer() throws Exception
|
||||
{
|
||||
server.stop();
|
||||
}
|
||||
|
||||
@Rule
|
||||
public TestName testname = new TestName();
|
||||
|
||||
private WebSocketClient client;
|
||||
|
||||
@Before
|
||||
public void startClient() throws Exception
|
||||
{
|
||||
client = new WebSocketClient();
|
||||
client.start();
|
||||
}
|
||||
|
||||
@After
|
||||
public void stopClient() throws Exception
|
||||
{
|
||||
client.stop();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpgradeWithWebkitDeflateExtension() throws Exception
|
||||
{
|
||||
Assume.assumeTrue("Server has x-webkit-deflate-frame registered",
|
||||
server.getWebSocketServletFactory().getExtensionFactory().isAvailable("x-webkit-deflate-frame"));
|
||||
|
||||
URI wsUri = server.getServerUri();
|
||||
|
||||
TrackingEndpoint clientSocket = new TrackingEndpoint(testname.getMethodName());
|
||||
ClientUpgradeRequest upgradeRequest = new ClientUpgradeRequest();
|
||||
upgradeRequest.addExtensions("x-webkit-deflate-frame");
|
||||
upgradeRequest.setSubProtocols("chat");
|
||||
Future<Session> clientConnectFuture = client.connect(clientSocket, wsUri, upgradeRequest);
|
||||
|
||||
Session clientSession = clientConnectFuture.get(Defaults.CONNECT_TIMEOUT_MS, TimeUnit.MILLISECONDS);
|
||||
List<ExtensionConfig> extensionConfigList = clientSession.getUpgradeResponse().getExtensions();
|
||||
assertThat("Client Upgrade Response.Extensions", extensionConfigList.size(), is(1));
|
||||
assertThat("Client Upgrade Response.Extensions[0]", extensionConfigList.get(0).toString(), containsString("x-webkit-deflate-frame"));
|
||||
|
||||
// Message
|
||||
String msg = "this is an echo ... cho ... ho ... o";
|
||||
clientSession.getRemote().sendString(msg);
|
||||
|
||||
// Read message
|
||||
String incomingMsg = clientSocket.messageQueue.poll(5, TimeUnit.SECONDS);
|
||||
Assert.assertThat("Incoming Message", incomingMsg, is(msg));
|
||||
|
||||
clientSession.close();
|
||||
}
|
||||
}
|
|
@ -16,34 +16,42 @@
|
|||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.websocket.server;
|
||||
package org.eclipse.jetty.websocket.tests.server;
|
||||
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
import java.net.URI;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import javax.servlet.ServletContext;
|
||||
|
||||
import org.eclipse.jetty.servlet.ServletContextHandler;
|
||||
import org.eclipse.jetty.toolchain.test.EventQueue;
|
||||
import org.eclipse.jetty.util.DecoratedObjectFactory;
|
||||
import org.eclipse.jetty.util.Decorator;
|
||||
import org.eclipse.jetty.websocket.api.Session;
|
||||
import org.eclipse.jetty.websocket.api.WebSocketAdapter;
|
||||
import org.eclipse.jetty.websocket.common.WebSocketFrame;
|
||||
import org.eclipse.jetty.websocket.common.frames.TextFrame;
|
||||
import org.eclipse.jetty.websocket.common.test.XBlockheadClient;
|
||||
import org.eclipse.jetty.websocket.client.ClientUpgradeRequest;
|
||||
import org.eclipse.jetty.websocket.client.WebSocketClient;
|
||||
import org.eclipse.jetty.websocket.servlet.ServletUpgradeRequest;
|
||||
import org.eclipse.jetty.websocket.servlet.ServletUpgradeResponse;
|
||||
import org.eclipse.jetty.websocket.servlet.WebSocketCreator;
|
||||
import org.eclipse.jetty.websocket.servlet.WebSocketServlet;
|
||||
import org.eclipse.jetty.websocket.servlet.WebSocketServletFactory;
|
||||
import org.eclipse.jetty.websocket.tests.Defaults;
|
||||
import org.eclipse.jetty.websocket.tests.SimpleServletServer;
|
||||
import org.eclipse.jetty.websocket.tests.TrackingEndpoint;
|
||||
import org.junit.After;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.TestName;
|
||||
|
||||
public class DecoratorsLegacyTest
|
||||
{
|
||||
|
@ -66,42 +74,42 @@ public class DecoratorsLegacyTest
|
|||
{
|
||||
out.printf("Object is a DecoratedObjectFactory%n");
|
||||
List<Decorator> decorators = objFactory.getDecorators();
|
||||
out.printf("Decorators.size = [%d]%n",decorators.size());
|
||||
out.printf("Decorators.size = [%d]%n", decorators.size());
|
||||
for (Decorator decorator : decorators)
|
||||
{
|
||||
out.printf(" decorator[] = %s%n",decorator.getClass().getName());
|
||||
out.printf(" decorator[] = %s%n", decorator.getClass().getName());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
out.printf("DecoratedObjectFactory is NULL%n");
|
||||
}
|
||||
|
||||
|
||||
getRemote().sendStringByFuture(str.toString());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static class DecoratorsCreator implements WebSocketCreator
|
||||
{
|
||||
@Override
|
||||
public Object createWebSocket(ServletUpgradeRequest req, ServletUpgradeResponse resp)
|
||||
{
|
||||
ServletContext servletContext = req.getHttpServletRequest().getServletContext();
|
||||
DecoratedObjectFactory objFactory = (DecoratedObjectFactory)servletContext.getAttribute(DecoratedObjectFactory.ATTR);
|
||||
DecoratedObjectFactory objFactory = (DecoratedObjectFactory) servletContext.getAttribute(DecoratedObjectFactory.ATTR);
|
||||
return new DecoratorsSocket(objFactory);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static class DecoratorsRequestServlet extends WebSocketServlet
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
private final WebSocketCreator creator;
|
||||
|
||||
|
||||
public DecoratorsRequestServlet(WebSocketCreator creator)
|
||||
{
|
||||
this.creator = creator;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void configure(WebSocketServletFactory factory)
|
||||
{
|
||||
|
@ -117,16 +125,16 @@ public class DecoratorsLegacyTest
|
|||
{
|
||||
return o;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void destroy(Object o)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static SimpleServletServer server;
|
||||
private static DecoratorsCreator decoratorsCreator;
|
||||
|
||||
|
||||
@BeforeClass
|
||||
public static void startServer() throws Exception
|
||||
{
|
||||
|
@ -144,38 +152,53 @@ public class DecoratorsLegacyTest
|
|||
};
|
||||
server.start();
|
||||
}
|
||||
|
||||
|
||||
@AfterClass
|
||||
public static void stopServer()
|
||||
public static void stopServer() throws Exception
|
||||
{
|
||||
server.stop();
|
||||
}
|
||||
|
||||
|
||||
@Rule
|
||||
public TestName testname = new TestName();
|
||||
|
||||
private WebSocketClient client;
|
||||
|
||||
@Before
|
||||
public void startClient() throws Exception
|
||||
{
|
||||
client = new WebSocketClient();
|
||||
client.start();
|
||||
}
|
||||
|
||||
@After
|
||||
public void stopClient() throws Exception
|
||||
{
|
||||
client.stop();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAccessRequestCookies() throws Exception
|
||||
{
|
||||
XBlockheadClient client = new XBlockheadClient(server.getServerUri());
|
||||
client.setTimeout(1,TimeUnit.SECONDS);
|
||||
|
||||
try
|
||||
{
|
||||
client.connect();
|
||||
client.sendStandardRequest();
|
||||
client.expectUpgradeResponse();
|
||||
|
||||
client.write(new TextFrame().setPayload("info"));
|
||||
|
||||
EventQueue<WebSocketFrame> frames = client.readFrames(1,1,TimeUnit.SECONDS);
|
||||
WebSocketFrame resp = frames.poll();
|
||||
String textMsg = resp.getPayloadAsUTF8();
|
||||
|
||||
assertThat("DecoratedObjectFactory", textMsg, containsString("Object is a DecoratedObjectFactory"));
|
||||
assertThat("decorators.size", textMsg, containsString("Decorators.size = [1]"));
|
||||
assertThat("decorator type", textMsg, containsString("decorator[] = " + DummyLegacyDecorator.class.getName()));
|
||||
}
|
||||
finally
|
||||
{
|
||||
client.close();
|
||||
}
|
||||
client.setMaxIdleTimeout(TimeUnit.SECONDS.toMillis(1));
|
||||
|
||||
URI wsUri = server.getServerUri();
|
||||
|
||||
TrackingEndpoint clientSocket = new TrackingEndpoint(testname.getMethodName());
|
||||
ClientUpgradeRequest upgradeRequest = new ClientUpgradeRequest();
|
||||
Future<Session> clientConnectFuture = client.connect(clientSocket, wsUri, upgradeRequest);
|
||||
|
||||
Session clientSession = clientConnectFuture.get(Defaults.CONNECT_TIMEOUT_MS, TimeUnit.MILLISECONDS);
|
||||
|
||||
// Request Info
|
||||
clientSession.getRemote().sendString("info");
|
||||
|
||||
// Read message
|
||||
String incomingMsg = clientSocket.messageQueue.poll(5, TimeUnit.SECONDS);
|
||||
assertThat("DecoratedObjectFactory", incomingMsg, containsString("Object is a DecoratedObjectFactory"));
|
||||
assertThat("decorators.size", incomingMsg, containsString("Decorators.size = [1]"));
|
||||
assertThat("decorator type", incomingMsg, containsString("decorator[] = " + DummyLegacyDecorator.class.getName()));
|
||||
|
||||
clientSession.close();
|
||||
}
|
||||
}
|
|
@ -16,34 +16,42 @@
|
|||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.websocket.server;
|
||||
package org.eclipse.jetty.websocket.tests.server;
|
||||
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
import java.net.URI;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import javax.servlet.ServletContext;
|
||||
|
||||
import org.eclipse.jetty.servlet.ServletContextHandler;
|
||||
import org.eclipse.jetty.toolchain.test.EventQueue;
|
||||
import org.eclipse.jetty.util.DecoratedObjectFactory;
|
||||
import org.eclipse.jetty.util.Decorator;
|
||||
import org.eclipse.jetty.websocket.api.Session;
|
||||
import org.eclipse.jetty.websocket.api.WebSocketAdapter;
|
||||
import org.eclipse.jetty.websocket.common.WebSocketFrame;
|
||||
import org.eclipse.jetty.websocket.common.frames.TextFrame;
|
||||
import org.eclipse.jetty.websocket.common.test.XBlockheadClient;
|
||||
import org.eclipse.jetty.websocket.client.ClientUpgradeRequest;
|
||||
import org.eclipse.jetty.websocket.client.WebSocketClient;
|
||||
import org.eclipse.jetty.websocket.servlet.ServletUpgradeRequest;
|
||||
import org.eclipse.jetty.websocket.servlet.ServletUpgradeResponse;
|
||||
import org.eclipse.jetty.websocket.servlet.WebSocketCreator;
|
||||
import org.eclipse.jetty.websocket.servlet.WebSocketServlet;
|
||||
import org.eclipse.jetty.websocket.servlet.WebSocketServletFactory;
|
||||
import org.eclipse.jetty.websocket.tests.Defaults;
|
||||
import org.eclipse.jetty.websocket.tests.SimpleServletServer;
|
||||
import org.eclipse.jetty.websocket.tests.TrackingEndpoint;
|
||||
import org.junit.After;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.TestName;
|
||||
|
||||
public class DecoratorsTest
|
||||
{
|
||||
|
@ -144,36 +152,51 @@ public class DecoratorsTest
|
|||
}
|
||||
|
||||
@AfterClass
|
||||
public static void stopServer()
|
||||
public static void stopServer() throws Exception
|
||||
{
|
||||
server.stop();
|
||||
}
|
||||
|
||||
@Rule
|
||||
public TestName testname = new TestName();
|
||||
|
||||
private WebSocketClient client;
|
||||
|
||||
@Before
|
||||
public void startClient() throws Exception
|
||||
{
|
||||
client = new WebSocketClient();
|
||||
client.start();
|
||||
}
|
||||
|
||||
@After
|
||||
public void stopClient() throws Exception
|
||||
{
|
||||
client.stop();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAccessRequestCookies() throws Exception
|
||||
{
|
||||
XBlockheadClient client = new XBlockheadClient(server.getServerUri());
|
||||
client.setTimeout(1,TimeUnit.SECONDS);
|
||||
|
||||
try
|
||||
{
|
||||
client.connect();
|
||||
client.sendStandardRequest();
|
||||
client.expectUpgradeResponse();
|
||||
|
||||
client.write(new TextFrame().setPayload("info"));
|
||||
|
||||
EventQueue<WebSocketFrame> frames = client.readFrames(1,1,TimeUnit.SECONDS);
|
||||
WebSocketFrame resp = frames.poll();
|
||||
String textMsg = resp.getPayloadAsUTF8();
|
||||
|
||||
assertThat("DecoratedObjectFactory", textMsg, containsString("Object is a DecoratedObjectFactory"));
|
||||
assertThat("decorators.size", textMsg, containsString("Decorators.size = [1]"));
|
||||
assertThat("decorator type", textMsg, containsString("decorator[] = " + DummyUtilDecorator.class.getName()));
|
||||
}
|
||||
finally
|
||||
{
|
||||
client.close();
|
||||
}
|
||||
client.setMaxIdleTimeout(TimeUnit.SECONDS.toMillis(1));
|
||||
|
||||
URI wsUri = server.getServerUri();
|
||||
|
||||
TrackingEndpoint clientSocket = new TrackingEndpoint(testname.getMethodName());
|
||||
ClientUpgradeRequest upgradeRequest = new ClientUpgradeRequest();
|
||||
Future<Session> clientConnectFuture = client.connect(clientSocket, wsUri, upgradeRequest);
|
||||
|
||||
Session clientSession = clientConnectFuture.get(Defaults.CONNECT_TIMEOUT_MS, TimeUnit.MILLISECONDS);
|
||||
|
||||
// Request Info
|
||||
clientSession.getRemote().sendString("info");
|
||||
|
||||
// Read message
|
||||
String incomingMsg = clientSocket.messageQueue.poll(5, TimeUnit.SECONDS);
|
||||
assertThat("DecoratedObjectFactory", incomingMsg, containsString("Object is a DecoratedObjectFactory"));
|
||||
assertThat("decorators.size", incomingMsg, containsString("Decorators.size = [1]"));
|
||||
assertThat("decorator type", incomingMsg, containsString("decorator[] = " + DummyUtilDecorator.class.getName()));
|
||||
|
||||
clientSession.close();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,109 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// All rights reserved. This program and the accompanying materials
|
||||
// are made available under the terms of the Eclipse Public License v1.0
|
||||
// and Apache License v2.0 which accompanies this distribution.
|
||||
//
|
||||
// The Eclipse Public License is available at
|
||||
// http://www.eclipse.org/legal/epl-v10.html
|
||||
//
|
||||
// The Apache License v2.0 is available at
|
||||
// http://www.opensource.org/licenses/apache2.0.php
|
||||
//
|
||||
// You may elect to redistribute this code under either of these licenses.
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.websocket.tests.server;
|
||||
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.eclipse.jetty.websocket.api.Session;
|
||||
import org.eclipse.jetty.websocket.api.extensions.ExtensionConfig;
|
||||
import org.eclipse.jetty.websocket.client.ClientUpgradeRequest;
|
||||
import org.eclipse.jetty.websocket.client.WebSocketClient;
|
||||
import org.eclipse.jetty.websocket.tests.Defaults;
|
||||
import org.eclipse.jetty.websocket.tests.SimpleServletServer;
|
||||
import org.eclipse.jetty.websocket.tests.TrackingEndpoint;
|
||||
import org.eclipse.jetty.websocket.tests.servlets.EchoServlet;
|
||||
import org.junit.After;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.TestName;
|
||||
|
||||
public class FirefoxTest
|
||||
{
|
||||
private static SimpleServletServer server;
|
||||
|
||||
@BeforeClass
|
||||
public static void startServer() throws Exception
|
||||
{
|
||||
server = new SimpleServletServer(new EchoServlet());
|
||||
server.start();
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void stopServer() throws Exception
|
||||
{
|
||||
server.stop();
|
||||
}
|
||||
|
||||
@Rule
|
||||
public TestName testname = new TestName();
|
||||
|
||||
private WebSocketClient client;
|
||||
|
||||
@Before
|
||||
public void startClient() throws Exception
|
||||
{
|
||||
client = new WebSocketClient();
|
||||
client.start();
|
||||
}
|
||||
|
||||
@After
|
||||
public void stopClient() throws Exception
|
||||
{
|
||||
client.stop();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConnectionKeepAlive() throws Exception
|
||||
{
|
||||
URI wsUri = server.getServerUri();
|
||||
|
||||
TrackingEndpoint clientSocket = new TrackingEndpoint(testname.getMethodName());
|
||||
ClientUpgradeRequest upgradeRequest = new ClientUpgradeRequest();
|
||||
// Odd Connection Header value seen in Firefox
|
||||
upgradeRequest.setHeader("Connection", "keep-alive, Upgrade");
|
||||
Future<Session> clientConnectFuture = client.connect(clientSocket, wsUri, upgradeRequest);
|
||||
|
||||
Session clientSession = clientConnectFuture.get(Defaults.CONNECT_TIMEOUT_MS, TimeUnit.MILLISECONDS);
|
||||
List<ExtensionConfig> extensionConfigList = clientSession.getUpgradeResponse().getExtensions();
|
||||
assertThat("Client Upgrade Response.Extensions", extensionConfigList.size(), is(1));
|
||||
assertThat("Client Upgrade Response.Extensions[0]", extensionConfigList.get(0).toString(), containsString("x-webkit-deflate-frame"));
|
||||
|
||||
// Message
|
||||
String msg = "this is an echo ... cho ... ho ... o";
|
||||
clientSession.getRemote().sendString(msg);
|
||||
|
||||
// Read message
|
||||
String incomingMsg = clientSocket.messageQueue.poll(5, TimeUnit.SECONDS);
|
||||
Assert.assertThat("Incoming Message", incomingMsg, is(msg));
|
||||
|
||||
clientSession.close();
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,133 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// All rights reserved. This program and the accompanying materials
|
||||
// are made available under the terms of the Eclipse Public License v1.0
|
||||
// and Apache License v2.0 which accompanies this distribution.
|
||||
//
|
||||
// The Eclipse Public License is available at
|
||||
// http://www.eclipse.org/legal/epl-v10.html
|
||||
//
|
||||
// The Apache License v2.0 is available at
|
||||
// http://www.opensource.org/licenses/apache2.0.php
|
||||
//
|
||||
// You may elect to redistribute this code under either of these licenses.
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.websocket.tests.server;
|
||||
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.eclipse.jetty.websocket.api.Session;
|
||||
import org.eclipse.jetty.websocket.api.extensions.ExtensionConfig;
|
||||
import org.eclipse.jetty.websocket.client.ClientUpgradeRequest;
|
||||
import org.eclipse.jetty.websocket.client.WebSocketClient;
|
||||
import org.eclipse.jetty.websocket.common.WebSocketFrame;
|
||||
import org.eclipse.jetty.websocket.tests.Defaults;
|
||||
import org.eclipse.jetty.websocket.tests.SimpleServletServer;
|
||||
import org.eclipse.jetty.websocket.tests.TrackingEndpoint;
|
||||
import org.eclipse.jetty.websocket.tests.servlets.EchoServlet;
|
||||
import org.junit.After;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Assume;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.TestName;
|
||||
|
||||
public class FragmentExtensionTest
|
||||
{
|
||||
private static SimpleServletServer server;
|
||||
|
||||
@BeforeClass
|
||||
public static void startServer() throws Exception
|
||||
{
|
||||
server = new SimpleServletServer(new EchoServlet());
|
||||
server.start();
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void stopServer() throws Exception
|
||||
{
|
||||
server.stop();
|
||||
}
|
||||
|
||||
@Rule
|
||||
public TestName testname = new TestName();
|
||||
|
||||
private WebSocketClient client;
|
||||
|
||||
@Before
|
||||
public void startClient() throws Exception
|
||||
{
|
||||
client = new WebSocketClient();
|
||||
client.start();
|
||||
}
|
||||
|
||||
@After
|
||||
public void stopClient() throws Exception
|
||||
{
|
||||
client.stop();
|
||||
}
|
||||
|
||||
private String[] split(String str, int partSize)
|
||||
{
|
||||
int strLength = str.length();
|
||||
int count = (int) Math.ceil((double) str.length() / partSize);
|
||||
String ret[] = new String[count];
|
||||
int idx;
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
idx = (i * partSize);
|
||||
ret[i] = str.substring(idx, Math.min(idx + partSize, strLength));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFragmentExtension() throws Exception
|
||||
{
|
||||
Assume.assumeTrue("Server has fragment registered",
|
||||
server.getWebSocketServletFactory().getExtensionFactory().isAvailable("fragment"));
|
||||
|
||||
int fragSize = 4;
|
||||
|
||||
URI wsUri = server.getServerUri();
|
||||
|
||||
TrackingEndpoint clientSocket = new TrackingEndpoint(testname.getMethodName());
|
||||
ClientUpgradeRequest upgradeRequest = new ClientUpgradeRequest();
|
||||
upgradeRequest.addExtensions("fragment;maxLength=" + fragSize);
|
||||
upgradeRequest.setSubProtocols("onConnect");
|
||||
Future<Session> clientConnectFuture = client.connect(clientSocket, wsUri, upgradeRequest);
|
||||
|
||||
Session clientSession = clientConnectFuture.get(Defaults.CONNECT_TIMEOUT_MS, TimeUnit.MILLISECONDS);
|
||||
List<ExtensionConfig> extensionConfigList = clientSession.getUpgradeResponse().getExtensions();
|
||||
assertThat("Client Upgrade Response.Extensions", extensionConfigList.size(), is(1));
|
||||
assertThat("Client Upgrade Response.Extensions[0]", extensionConfigList.get(0).toString(), containsString("fragment"));
|
||||
|
||||
// Message
|
||||
String msg = "Sent as a long message that should be split";
|
||||
clientSession.getRemote().sendString(msg);
|
||||
|
||||
// Read message
|
||||
String parts[] = split(msg, fragSize);
|
||||
for (int i = 0; i < parts.length; i++)
|
||||
{
|
||||
WebSocketFrame frame = clientSocket.framesQueue.poll();
|
||||
Assert.assertThat("text[" + i + "].payload", frame.getPayloadAsUTF8(), is(parts[i]));
|
||||
}
|
||||
|
||||
clientSession.close();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,113 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// All rights reserved. This program and the accompanying materials
|
||||
// are made available under the terms of the Eclipse Public License v1.0
|
||||
// and Apache License v2.0 which accompanies this distribution.
|
||||
//
|
||||
// The Eclipse Public License is available at
|
||||
// http://www.eclipse.org/legal/epl-v10.html
|
||||
//
|
||||
// The Apache License v2.0 is available at
|
||||
// http://www.opensource.org/licenses/apache2.0.php
|
||||
//
|
||||
// You may elect to redistribute this code under either of these licenses.
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.websocket.tests.server;
|
||||
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.eclipse.jetty.websocket.api.Session;
|
||||
import org.eclipse.jetty.websocket.api.extensions.ExtensionConfig;
|
||||
import org.eclipse.jetty.websocket.client.ClientUpgradeRequest;
|
||||
import org.eclipse.jetty.websocket.client.WebSocketClient;
|
||||
import org.eclipse.jetty.websocket.tests.Defaults;
|
||||
import org.eclipse.jetty.websocket.tests.SimpleServletServer;
|
||||
import org.eclipse.jetty.websocket.tests.TrackingEndpoint;
|
||||
import org.eclipse.jetty.websocket.tests.servlets.EchoServlet;
|
||||
import org.junit.After;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Assume;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.TestName;
|
||||
|
||||
public class IdentityExtensionTest
|
||||
{
|
||||
private static SimpleServletServer server;
|
||||
|
||||
@BeforeClass
|
||||
public static void startServer() throws Exception
|
||||
{
|
||||
server = new SimpleServletServer(new EchoServlet());
|
||||
server.start();
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void stopServer() throws Exception
|
||||
{
|
||||
server.stop();
|
||||
}
|
||||
|
||||
@Rule
|
||||
public TestName testname = new TestName();
|
||||
|
||||
private WebSocketClient client;
|
||||
|
||||
@Before
|
||||
public void startClient() throws Exception
|
||||
{
|
||||
client = new WebSocketClient();
|
||||
client.start();
|
||||
}
|
||||
|
||||
@After
|
||||
public void stopClient() throws Exception
|
||||
{
|
||||
client.stop();
|
||||
}
|
||||
|
||||
@Test(timeout = 60000)
|
||||
public void testIdentityExtension() throws Exception
|
||||
{
|
||||
Assume.assumeTrue("Server has identity extension registered",
|
||||
server.getWebSocketServletFactory().getExtensionFactory().isAvailable("identity"));
|
||||
|
||||
URI wsUri = server.getServerUri();
|
||||
|
||||
TrackingEndpoint clientSocket = new TrackingEndpoint(testname.getMethodName());
|
||||
ClientUpgradeRequest upgradeRequest = new ClientUpgradeRequest();
|
||||
upgradeRequest.addExtensions("identity;param=0");
|
||||
upgradeRequest.addExtensions("identity;param=1, identity ; param = '2' ; other = ' some = value '");
|
||||
upgradeRequest.setSubProtocols("onConnect");
|
||||
Future<Session> clientConnectFuture = client.connect(clientSocket, wsUri, upgradeRequest);
|
||||
|
||||
Session clientSession = clientConnectFuture.get(Defaults.CONNECT_TIMEOUT_MS, TimeUnit.MILLISECONDS);
|
||||
List<ExtensionConfig> extensionConfigList = clientSession.getUpgradeResponse().getExtensions();
|
||||
assertThat("Client Upgrade Response.Extensions", extensionConfigList.size(), is(3));
|
||||
assertThat("Client Upgrade Response.Extensions[0]", extensionConfigList.get(0).toString(), containsString("identity"));
|
||||
|
||||
// Message
|
||||
String msg = "Hello Identity";
|
||||
clientSession.getRemote().sendString(msg);
|
||||
|
||||
// Read message
|
||||
String incomingMsg = clientSocket.messageQueue.poll(5, TimeUnit.SECONDS);
|
||||
Assert.assertThat("Incoming Message", incomingMsg, is(msg));
|
||||
|
||||
clientSession.close();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,131 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// All rights reserved. This program and the accompanying materials
|
||||
// are made available under the terms of the Eclipse Public License v1.0
|
||||
// and Apache License v2.0 which accompanies this distribution.
|
||||
//
|
||||
// The Eclipse Public License is available at
|
||||
// http://www.eclipse.org/legal/epl-v10.html
|
||||
//
|
||||
// The Apache License v2.0 is available at
|
||||
// http://www.opensource.org/licenses/apache2.0.php
|
||||
//
|
||||
// You may elect to redistribute this code under either of these licenses.
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.websocket.tests.server;
|
||||
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.eclipse.jetty.websocket.api.Session;
|
||||
import org.eclipse.jetty.websocket.api.StatusCode;
|
||||
import org.eclipse.jetty.websocket.api.annotations.WebSocket;
|
||||
import org.eclipse.jetty.websocket.client.ClientUpgradeRequest;
|
||||
import org.eclipse.jetty.websocket.client.WebSocketClient;
|
||||
import org.eclipse.jetty.websocket.servlet.WebSocketServlet;
|
||||
import org.eclipse.jetty.websocket.servlet.WebSocketServletFactory;
|
||||
import org.eclipse.jetty.websocket.tests.Defaults;
|
||||
import org.eclipse.jetty.websocket.tests.SimpleServletServer;
|
||||
import org.eclipse.jetty.websocket.tests.TrackingEndpoint;
|
||||
import org.junit.After;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.TestName;
|
||||
|
||||
public class IdleTimeoutTest
|
||||
{
|
||||
@WebSocket(maxIdleTime = 500)
|
||||
public static class FastTimeoutRFCSocket extends RFC6455Socket
|
||||
{
|
||||
}
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public static class TimeoutServlet extends WebSocketServlet
|
||||
{
|
||||
@Override
|
||||
public void configure(WebSocketServletFactory factory)
|
||||
{
|
||||
factory.register(FastTimeoutRFCSocket.class);
|
||||
}
|
||||
}
|
||||
|
||||
private static SimpleServletServer server;
|
||||
|
||||
@BeforeClass
|
||||
public static void startServer() throws Exception
|
||||
{
|
||||
server = new SimpleServletServer(new TimeoutServlet());
|
||||
server.start();
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void stopServer() throws Exception
|
||||
{
|
||||
server.stop();
|
||||
}
|
||||
|
||||
@Rule
|
||||
public TestName testname = new TestName();
|
||||
|
||||
private WebSocketClient client;
|
||||
|
||||
@Before
|
||||
public void startClient() throws Exception
|
||||
{
|
||||
client = new WebSocketClient();
|
||||
client.start();
|
||||
}
|
||||
|
||||
@After
|
||||
public void stopClient() throws Exception
|
||||
{
|
||||
client.stop();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test IdleTimeout on server.
|
||||
*
|
||||
* @throws Exception on test failure
|
||||
*/
|
||||
@Test
|
||||
public void testIdleTimeout() throws Exception
|
||||
{
|
||||
client.setMaxIdleTimeout(2500);
|
||||
URI wsUri = server.getServerUri();
|
||||
|
||||
TrackingEndpoint clientSocket = new TrackingEndpoint(testname.getMethodName());
|
||||
ClientUpgradeRequest upgradeRequest = new ClientUpgradeRequest();
|
||||
upgradeRequest.setSubProtocols("onConnect");
|
||||
Future<Session> clientConnectFuture = client.connect(clientSocket, wsUri, upgradeRequest);
|
||||
|
||||
Session clientSession = clientConnectFuture.get(Defaults.CONNECT_TIMEOUT_MS, TimeUnit.MILLISECONDS);
|
||||
|
||||
// This wait should be shorter than client timeout above, but
|
||||
// longer than server timeout configured in FastTimeoutRFCSocket
|
||||
// eg: websocket server endpoint timeout < this timeout < websocket client idle timeout
|
||||
TimeUnit.MILLISECONDS.sleep(1000);
|
||||
|
||||
// Write to server
|
||||
// This action is possible, but does nothing.
|
||||
// Server could be in a half-closed state at this point.
|
||||
// Where the server read is closed (due to timeout), but the server write is still open.
|
||||
// The server could not read this frame, if it is in this half closed state
|
||||
clientSession.getRemote().sendString("Hello");
|
||||
|
||||
// Expect closure, as server should have timed out
|
||||
clientSocket.awaitCloseEvent("Client");
|
||||
clientSocket.assertCloseInfo("Client", StatusCode.SHUTDOWN, containsString("Timeout"));
|
||||
|
||||
clientSession.close();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// All rights reserved. This program and the accompanying materials
|
||||
// are made available under the terms of the Eclipse Public License v1.0
|
||||
// and Apache License v2.0 which accompanies this distribution.
|
||||
//
|
||||
// The Eclipse Public License is available at
|
||||
// http://www.eclipse.org/legal/epl-v10.html
|
||||
//
|
||||
// The Apache License v2.0 is available at
|
||||
// http://www.opensource.org/licenses/apache2.0.php
|
||||
//
|
||||
// You may elect to redistribute this code under either of these licenses.
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.websocket.tests.server;
|
||||
|
||||
import javax.servlet.ServletContextEvent;
|
||||
import javax.servlet.ServletContextListener;
|
||||
|
||||
import org.eclipse.jetty.websocket.server.NativeWebSocketConfiguration;
|
||||
import org.eclipse.jetty.websocket.servlet.ServletUpgradeRequest;
|
||||
import org.eclipse.jetty.websocket.servlet.ServletUpgradeResponse;
|
||||
import org.eclipse.jetty.websocket.servlet.WebSocketCreator;
|
||||
|
||||
public class InfoContextAltAttributeListener implements WebSocketCreator, ServletContextListener
|
||||
{
|
||||
private static final String ATTR = "alt.config";
|
||||
|
||||
@Override
|
||||
public void contextInitialized(ServletContextEvent sce)
|
||||
{
|
||||
NativeWebSocketConfiguration configuration = new NativeWebSocketConfiguration(sce.getServletContext());
|
||||
configuration.getFactory().getPolicy().setMaxTextMessageSize(10 * 1024 * 1024);
|
||||
configuration.addMapping("/info/*", this);
|
||||
sce.getServletContext().setAttribute(ATTR, configuration);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void contextDestroyed(ServletContextEvent sce)
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object createWebSocket(ServletUpgradeRequest req, ServletUpgradeResponse resp)
|
||||
{
|
||||
return new InfoSocket();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// All rights reserved. This program and the accompanying materials
|
||||
// are made available under the terms of the Eclipse Public License v1.0
|
||||
// and Apache License v2.0 which accompanies this distribution.
|
||||
//
|
||||
// The Eclipse Public License is available at
|
||||
// http://www.eclipse.org/legal/epl-v10.html
|
||||
//
|
||||
// The Apache License v2.0 is available at
|
||||
// http://www.opensource.org/licenses/apache2.0.php
|
||||
//
|
||||
// You may elect to redistribute this code under either of these licenses.
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.websocket.tests.server;
|
||||
|
||||
import javax.servlet.ServletContextEvent;
|
||||
import javax.servlet.ServletContextListener;
|
||||
|
||||
import org.eclipse.jetty.websocket.server.NativeWebSocketConfiguration;
|
||||
import org.eclipse.jetty.websocket.servlet.ServletUpgradeRequest;
|
||||
import org.eclipse.jetty.websocket.servlet.ServletUpgradeResponse;
|
||||
import org.eclipse.jetty.websocket.servlet.WebSocketCreator;
|
||||
|
||||
public class InfoContextAttributeListener implements WebSocketCreator, ServletContextListener
|
||||
{
|
||||
@Override
|
||||
public void contextInitialized(ServletContextEvent sce)
|
||||
{
|
||||
NativeWebSocketConfiguration configuration = (NativeWebSocketConfiguration) sce.getServletContext().getAttribute(NativeWebSocketConfiguration.class.getName());
|
||||
configuration.getFactory().getPolicy().setMaxTextMessageSize(10 * 1024 * 1024);
|
||||
configuration.addMapping("/info/*", this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void contextDestroyed(ServletContextEvent sce)
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object createWebSocket(ServletUpgradeRequest req, ServletUpgradeResponse resp)
|
||||
{
|
||||
return new InfoSocket();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// All rights reserved. This program and the accompanying materials
|
||||
// are made available under the terms of the Eclipse Public License v1.0
|
||||
// and Apache License v2.0 which accompanies this distribution.
|
||||
//
|
||||
// The Eclipse Public License is available at
|
||||
// http://www.eclipse.org/legal/epl-v10.html
|
||||
//
|
||||
// The Apache License v2.0 is available at
|
||||
// http://www.opensource.org/licenses/apache2.0.php
|
||||
//
|
||||
// You may elect to redistribute this code under either of these licenses.
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.websocket.tests.server;
|
||||
|
||||
import javax.servlet.ServletContextEvent;
|
||||
import javax.servlet.ServletContextListener;
|
||||
|
||||
import org.eclipse.jetty.websocket.server.NativeWebSocketConfiguration;
|
||||
import org.eclipse.jetty.websocket.servlet.ServletUpgradeRequest;
|
||||
import org.eclipse.jetty.websocket.servlet.ServletUpgradeResponse;
|
||||
import org.eclipse.jetty.websocket.servlet.WebSocketCreator;
|
||||
|
||||
public class InfoContextListener implements WebSocketCreator, ServletContextListener
|
||||
{
|
||||
@Override
|
||||
public void contextInitialized(ServletContextEvent sce)
|
||||
{
|
||||
NativeWebSocketConfiguration configuration = new NativeWebSocketConfiguration(sce.getServletContext());
|
||||
configuration.getFactory().getPolicy().setMaxTextMessageSize(10 * 1024 * 1024);
|
||||
configuration.addMapping("/info/*", this);
|
||||
sce.getServletContext().setAttribute(NativeWebSocketConfiguration.class.getName(), configuration);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void contextDestroyed(ServletContextEvent sce)
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object createWebSocket(ServletUpgradeRequest req, ServletUpgradeResponse resp)
|
||||
{
|
||||
return new InfoSocket();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// All rights reserved. This program and the accompanying materials
|
||||
// are made available under the terms of the Eclipse Public License v1.0
|
||||
// and Apache License v2.0 which accompanies this distribution.
|
||||
//
|
||||
// The Eclipse Public License is available at
|
||||
// http://www.eclipse.org/legal/epl-v10.html
|
||||
//
|
||||
// The Apache License v2.0 is available at
|
||||
// http://www.opensource.org/licenses/apache2.0.php
|
||||
//
|
||||
// You may elect to redistribute this code under either of these licenses.
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.websocket.tests.server;
|
||||
|
||||
import javax.servlet.ServletConfig;
|
||||
import javax.servlet.ServletContext;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServlet;
|
||||
|
||||
import org.eclipse.jetty.websocket.server.NativeWebSocketConfiguration;
|
||||
import org.eclipse.jetty.websocket.servlet.ServletUpgradeRequest;
|
||||
import org.eclipse.jetty.websocket.servlet.ServletUpgradeResponse;
|
||||
import org.eclipse.jetty.websocket.servlet.WebSocketCreator;
|
||||
|
||||
public class InfoServlet extends HttpServlet implements WebSocketCreator
|
||||
{
|
||||
@Override
|
||||
public Object createWebSocket(ServletUpgradeRequest req, ServletUpgradeResponse resp)
|
||||
{
|
||||
return new InfoSocket();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(ServletConfig config) throws ServletException
|
||||
{
|
||||
ServletContext context = config.getServletContext();
|
||||
NativeWebSocketConfiguration configuration = (NativeWebSocketConfiguration) context.getAttribute(NativeWebSocketConfiguration.class.getName());
|
||||
configuration.getFactory().getPolicy().setMaxTextMessageSize(10 * 1024 * 1024);
|
||||
configuration.addMapping("/info/*", this);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// All rights reserved. This program and the accompanying materials
|
||||
// are made available under the terms of the Eclipse Public License v1.0
|
||||
// and Apache License v2.0 which accompanies this distribution.
|
||||
//
|
||||
// The Eclipse Public License is available at
|
||||
// http://www.eclipse.org/legal/epl-v10.html
|
||||
//
|
||||
// The Apache License v2.0 is available at
|
||||
// http://www.opensource.org/licenses/apache2.0.php
|
||||
//
|
||||
// You may elect to redistribute this code under either of these licenses.
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.websocket.tests.server;
|
||||
|
||||
import org.eclipse.jetty.websocket.api.RemoteEndpoint;
|
||||
import org.eclipse.jetty.websocket.api.Session;
|
||||
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketConnect;
|
||||
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage;
|
||||
import org.eclipse.jetty.websocket.api.annotations.WebSocket;
|
||||
|
||||
@WebSocket
|
||||
public class InfoSocket
|
||||
{
|
||||
private Session session;
|
||||
|
||||
@OnWebSocketConnect
|
||||
public void onConnect(Session session)
|
||||
{
|
||||
this.session = session;
|
||||
}
|
||||
|
||||
@OnWebSocketMessage
|
||||
public void onMessage(String msg)
|
||||
{
|
||||
RemoteEndpoint remote = this.session.getRemote();
|
||||
remote.sendStringByFuture("session.maxTextMessageSize=" + session.getPolicy().getMaxTextMessageSize());
|
||||
}
|
||||
}
|
|
@ -16,43 +16,47 @@
|
|||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.websocket.server;
|
||||
package org.eclipse.jetty.websocket.tests.server;
|
||||
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.hamcrest.Matchers.anything;
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import org.eclipse.jetty.toolchain.test.EventQueue;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
import org.eclipse.jetty.util.log.StacklessLogging;
|
||||
import org.eclipse.jetty.util.log.StdErrLog;
|
||||
import org.eclipse.jetty.websocket.api.Session;
|
||||
import org.eclipse.jetty.websocket.api.StatusCode;
|
||||
import org.eclipse.jetty.websocket.api.WebSocketAdapter;
|
||||
import org.eclipse.jetty.websocket.common.CloseInfo;
|
||||
import org.eclipse.jetty.websocket.common.OpCode;
|
||||
import org.eclipse.jetty.websocket.common.WebSocketFrame;
|
||||
import org.eclipse.jetty.websocket.client.ClientUpgradeRequest;
|
||||
import org.eclipse.jetty.websocket.client.WebSocketClient;
|
||||
import org.eclipse.jetty.websocket.common.WebSocketSession;
|
||||
import org.eclipse.jetty.websocket.common.frames.TextFrame;
|
||||
import org.eclipse.jetty.websocket.common.test.XBlockheadClient;
|
||||
import org.eclipse.jetty.websocket.common.test.IBlockheadClient;
|
||||
import org.eclipse.jetty.websocket.server.helper.RFCSocket;
|
||||
import org.eclipse.jetty.websocket.server.WebSocketServerFactory;
|
||||
import org.eclipse.jetty.websocket.servlet.ServletUpgradeRequest;
|
||||
import org.eclipse.jetty.websocket.servlet.ServletUpgradeResponse;
|
||||
import org.eclipse.jetty.websocket.servlet.WebSocketCreator;
|
||||
import org.eclipse.jetty.websocket.servlet.WebSocketServlet;
|
||||
import org.eclipse.jetty.websocket.servlet.WebSocketServletFactory;
|
||||
import org.eclipse.jetty.websocket.tests.Defaults;
|
||||
import org.eclipse.jetty.websocket.tests.SimpleServletServer;
|
||||
import org.eclipse.jetty.websocket.tests.TrackingEndpoint;
|
||||
import org.junit.After;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.TestName;
|
||||
|
||||
/**
|
||||
* Tests various close scenarios that should result in Open Session cleanup
|
||||
|
@ -66,39 +70,39 @@ public class ManyConnectionsCleanupTest
|
|||
public String closeReason = null;
|
||||
public int closeStatusCode = -1;
|
||||
public List<Throwable> errors = new ArrayList<>();
|
||||
|
||||
|
||||
@Override
|
||||
public void onWebSocketClose(int statusCode, String reason)
|
||||
{
|
||||
LOG.debug("onWebSocketClose({}, {})",statusCode,reason);
|
||||
LOG.debug("onWebSocketClose({}, {})", statusCode, reason);
|
||||
this.closeStatusCode = statusCode;
|
||||
this.closeReason = reason;
|
||||
closeLatch.countDown();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onWebSocketError(Throwable cause)
|
||||
{
|
||||
errors.add(cause);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public static class CloseServlet extends WebSocketServlet implements WebSocketCreator
|
||||
{
|
||||
private WebSocketServerFactory serverFactory;
|
||||
private AtomicInteger calls = new AtomicInteger(0);
|
||||
|
||||
|
||||
@Override
|
||||
public void configure(WebSocketServletFactory factory)
|
||||
{
|
||||
factory.setCreator(this);
|
||||
if (factory instanceof WebSocketServerFactory)
|
||||
{
|
||||
this.serverFactory = (WebSocketServerFactory)factory;
|
||||
this.serverFactory = (WebSocketServerFactory) factory;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Object createWebSocket(ServletUpgradeRequest req, ServletUpgradeResponse resp)
|
||||
{
|
||||
|
@ -107,22 +111,22 @@ public class ManyConnectionsCleanupTest
|
|||
closeSocket = new FastCloseSocket(calls);
|
||||
return closeSocket;
|
||||
}
|
||||
|
||||
|
||||
if (req.hasSubProtocol("fastfail"))
|
||||
{
|
||||
closeSocket = new FastFailSocket(calls);
|
||||
return closeSocket;
|
||||
}
|
||||
|
||||
|
||||
if (req.hasSubProtocol("container"))
|
||||
{
|
||||
closeSocket = new ContainerSocket(serverFactory,calls);
|
||||
closeSocket = new ContainerSocket(serverFactory, calls);
|
||||
return closeSocket;
|
||||
}
|
||||
return new RFCSocket();
|
||||
return new RFC6455Socket();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* On Message, return container information
|
||||
*/
|
||||
|
@ -132,22 +136,22 @@ public class ManyConnectionsCleanupTest
|
|||
private final WebSocketServerFactory container;
|
||||
private final AtomicInteger calls;
|
||||
private Session session;
|
||||
|
||||
|
||||
public ContainerSocket(WebSocketServerFactory container, AtomicInteger calls)
|
||||
{
|
||||
this.container = container;
|
||||
this.calls = calls;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onWebSocketText(String message)
|
||||
{
|
||||
LOG.debug("onWebSocketText({})",message);
|
||||
LOG.debug("onWebSocketText({})", message);
|
||||
calls.incrementAndGet();
|
||||
if (message.equalsIgnoreCase("openSessions"))
|
||||
{
|
||||
Collection<WebSocketSession> sessions = container.getOpenSessions();
|
||||
|
||||
|
||||
StringBuilder ret = new StringBuilder();
|
||||
ret.append("openSessions.size=").append(sessions.size()).append('\n');
|
||||
int idx = 0;
|
||||
|
@ -156,21 +160,22 @@ public class ManyConnectionsCleanupTest
|
|||
ret.append('[').append(idx++).append("] ").append(sess.toString()).append('\n');
|
||||
}
|
||||
session.getRemote().sendStringByFuture(ret.toString());
|
||||
session.close(StatusCode.NORMAL,"ContainerSocket");
|
||||
} else if(message.equalsIgnoreCase("calls"))
|
||||
session.close(StatusCode.NORMAL, "ContainerSocket");
|
||||
}
|
||||
else if (message.equalsIgnoreCase("calls"))
|
||||
{
|
||||
session.getRemote().sendStringByFuture(String.format("calls=%,d",calls.get()));
|
||||
session.getRemote().sendStringByFuture(String.format("calls=%,d", calls.get()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onWebSocketConnect(Session sess)
|
||||
{
|
||||
LOG.debug("onWebSocketConnect({})",sess);
|
||||
LOG.debug("onWebSocketConnect({})", sess);
|
||||
this.session = sess;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* On Connect, close socket
|
||||
*/
|
||||
|
@ -178,21 +183,21 @@ public class ManyConnectionsCleanupTest
|
|||
{
|
||||
private static final Logger LOG = Log.getLogger(ManyConnectionsCleanupTest.FastCloseSocket.class);
|
||||
private final AtomicInteger calls;
|
||||
|
||||
|
||||
public FastCloseSocket(AtomicInteger calls)
|
||||
{
|
||||
this.calls = calls;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onWebSocketConnect(Session sess)
|
||||
{
|
||||
LOG.debug("onWebSocketConnect({})",sess);
|
||||
LOG.debug("onWebSocketConnect({})", sess);
|
||||
calls.incrementAndGet();
|
||||
sess.close(StatusCode.NORMAL,"FastCloseServer");
|
||||
sess.close(StatusCode.NORMAL, "FastCloseServer");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* On Connect, throw unhandled exception
|
||||
*/
|
||||
|
@ -200,46 +205,63 @@ public class ManyConnectionsCleanupTest
|
|||
{
|
||||
private static final Logger LOG = Log.getLogger(ManyConnectionsCleanupTest.FastFailSocket.class);
|
||||
private final AtomicInteger calls;
|
||||
|
||||
|
||||
public FastFailSocket(AtomicInteger calls)
|
||||
{
|
||||
this.calls = calls;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onWebSocketConnect(Session sess)
|
||||
{
|
||||
LOG.debug("onWebSocketConnect({})",sess);
|
||||
LOG.debug("onWebSocketConnect({})", sess);
|
||||
calls.incrementAndGet();
|
||||
// Test failure due to unhandled exception
|
||||
// this should trigger a fast-fail closure during open/connect
|
||||
throw new RuntimeException("Intentional FastFail");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static final Logger LOG = Log.getLogger(ManyConnectionsCleanupTest.class);
|
||||
|
||||
|
||||
private static SimpleServletServer server;
|
||||
private static AbstractCloseSocket closeSocket;
|
||||
|
||||
|
||||
@BeforeClass
|
||||
public static void startServer() throws Exception
|
||||
{
|
||||
server = new SimpleServletServer(new CloseServlet());
|
||||
server.start();
|
||||
}
|
||||
|
||||
|
||||
@AfterClass
|
||||
public static void stopServer()
|
||||
public static void stopServer() throws Exception
|
||||
{
|
||||
server.stop();
|
||||
}
|
||||
|
||||
|
||||
@Rule
|
||||
public TestName testname = new TestName();
|
||||
|
||||
private WebSocketClient client;
|
||||
|
||||
@Before
|
||||
public void startClient() throws Exception
|
||||
{
|
||||
client = new WebSocketClient();
|
||||
client.start();
|
||||
}
|
||||
|
||||
@After
|
||||
public void stopClient() throws Exception
|
||||
{
|
||||
client.stop();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test session open session cleanup (bug #474936)
|
||||
*
|
||||
* @throws Exception
|
||||
* on test failure
|
||||
* Test session tracking (open + close + cleanup) (bug #474936)
|
||||
*
|
||||
* @throws Exception on test failure
|
||||
*/
|
||||
@Test
|
||||
public void testOpenSessionCleanup() throws Exception
|
||||
|
@ -260,110 +282,81 @@ public class ManyConnectionsCleanupTest
|
|||
}
|
||||
|
||||
sessLog.setLevel(oldLevel);
|
||||
|
||||
try (IBlockheadClient client = new XBlockheadClient(server.getServerUri()))
|
||||
{
|
||||
client.setProtocols("container");
|
||||
client.setTimeout(1,TimeUnit.SECONDS);
|
||||
client.connect();
|
||||
client.sendStandardRequest();
|
||||
client.expectUpgradeResponse();
|
||||
|
||||
client.write(new TextFrame().setPayload("calls"));
|
||||
client.write(new TextFrame().setPayload("openSessions"));
|
||||
|
||||
EventQueue<WebSocketFrame> frames = client.readFrames(3,6,TimeUnit.SECONDS);
|
||||
WebSocketFrame frame;
|
||||
String resp;
|
||||
|
||||
frame = frames.poll();
|
||||
assertThat("frames[0].opcode",frame.getOpCode(),is(OpCode.TEXT));
|
||||
resp = frame.getPayloadAsUTF8();
|
||||
assertThat("Should only have 1 open session",resp,containsString("calls=" + ((iterationCount * 2) + 1)));
|
||||
|
||||
frame = frames.poll();
|
||||
assertThat("frames[1].opcode",frame.getOpCode(),is(OpCode.TEXT));
|
||||
resp = frame.getPayloadAsUTF8();
|
||||
assertThat("Should only have 1 open session",resp,containsString("openSessions.size=1\n"));
|
||||
|
||||
frame = frames.poll();
|
||||
assertThat("frames[2].opcode",frame.getOpCode(),is(OpCode.CLOSE));
|
||||
CloseInfo close = new CloseInfo(frame);
|
||||
assertThat("Close Status Code",close.getStatusCode(),is(StatusCode.NORMAL));
|
||||
client.write(close.asFrame()); // respond with close
|
||||
|
||||
// ensure server socket got close event
|
||||
assertThat("Open Sessions Latch",closeSocket.closeLatch.await(1,TimeUnit.SECONDS),is(true));
|
||||
assertThat("Open Sessions.statusCode",closeSocket.closeStatusCode,is(StatusCode.NORMAL));
|
||||
assertThat("Open Sessions.errors",closeSocket.errors.size(),is(0));
|
||||
}
|
||||
}
|
||||
|
||||
private void fastClose() throws Exception
|
||||
{
|
||||
try (IBlockheadClient client = new XBlockheadClient(server.getServerUri()))
|
||||
{
|
||||
client.setProtocols("fastclose");
|
||||
client.setTimeout(1,TimeUnit.SECONDS);
|
||||
try (StacklessLogging ignored = new StacklessLogging(WebSocketSession.class))
|
||||
{
|
||||
client.connect();
|
||||
client.sendStandardRequest();
|
||||
client.expectUpgradeResponse();
|
||||
|
||||
client.readFrames(1,1,TimeUnit.SECONDS);
|
||||
|
||||
CloseInfo close = new CloseInfo(StatusCode.NORMAL,"Normal");
|
||||
assertThat("Close Status Code",close.getStatusCode(),is(StatusCode.NORMAL));
|
||||
|
||||
// Notify server of close handshake
|
||||
client.write(close.asFrame()); // respond with close
|
||||
|
||||
// ensure server socket got close event
|
||||
assertThat("Fast Close Latch",closeSocket.closeLatch.await(1,TimeUnit.SECONDS),is(true));
|
||||
assertThat("Fast Close.statusCode",closeSocket.closeStatusCode,is(StatusCode.NORMAL));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void fastFail() throws Exception
|
||||
{
|
||||
try (IBlockheadClient client = new XBlockheadClient(server.getServerUri()))
|
||||
{
|
||||
client.setProtocols("fastfail");
|
||||
client.setTimeout(1,TimeUnit.SECONDS);
|
||||
try (StacklessLogging ignored = new StacklessLogging(WebSocketSession.class))
|
||||
{
|
||||
client.connect();
|
||||
client.sendStandardRequest();
|
||||
client.expectUpgradeResponse();
|
||||
|
||||
// client.readFrames(1,2,TimeUnit.SECONDS);
|
||||
|
||||
CloseInfo close = new CloseInfo(StatusCode.NORMAL,"Normal");
|
||||
client.write(close.asFrame()); // respond with close
|
||||
|
||||
// ensure server socket got close event
|
||||
assertThat("Fast Fail Latch",closeSocket.closeLatch.await(1,TimeUnit.SECONDS),is(true));
|
||||
assertThat("Fast Fail.statusCode",closeSocket.closeStatusCode,is(StatusCode.SERVER_ERROR));
|
||||
assertThat("Fast Fail.errors",closeSocket.errors.size(),is(1));
|
||||
}
|
||||
}
|
||||
|
||||
URI wsUri = server.getServerUri();
|
||||
|
||||
TrackingEndpoint clientSocket = new TrackingEndpoint(testname.getMethodName());
|
||||
ClientUpgradeRequest upgradeRequest = new ClientUpgradeRequest();
|
||||
upgradeRequest.setSubProtocols("container");
|
||||
Future<Session> clientConnectFuture = client.connect(clientSocket, wsUri, upgradeRequest);
|
||||
|
||||
Session clientSession = clientConnectFuture.get(Defaults.CONNECT_TIMEOUT_MS, TimeUnit.MILLISECONDS);
|
||||
|
||||
clientSession.getRemote().sendString("calls");
|
||||
clientSession.getRemote().sendString("openSessions");
|
||||
|
||||
String incomingMessage;
|
||||
incomingMessage = clientSocket.messageQueue.poll(5, TimeUnit.SECONDS);
|
||||
assertThat("Should only have 1 open session", incomingMessage, containsString("calls=" + ((iterationCount * 2) + 1)));
|
||||
|
||||
incomingMessage = clientSocket.messageQueue.poll(5, TimeUnit.SECONDS);
|
||||
assertThat("Should only have 1 open session", incomingMessage, containsString("openSessions.size=1\n"));
|
||||
|
||||
clientSocket.awaitCloseEvent("Client");
|
||||
clientSocket.assertCloseInfo("Client", StatusCode.NORMAL, anything());
|
||||
}
|
||||
|
||||
@SuppressWarnings("Duplicates")
|
||||
private void fastClose() throws Exception
|
||||
{
|
||||
client.setMaxIdleTimeout(1000);
|
||||
URI wsUri = server.getServerUri();
|
||||
|
||||
TrackingEndpoint clientSocket = new TrackingEndpoint(testname.getMethodName());
|
||||
ClientUpgradeRequest upgradeRequest = new ClientUpgradeRequest();
|
||||
upgradeRequest.setSubProtocols("fastclose");
|
||||
Future<Session> clientConnectFuture = client.connect(clientSocket, wsUri, upgradeRequest);
|
||||
|
||||
Session clientSession = clientConnectFuture.get(Defaults.CONNECT_TIMEOUT_MS, TimeUnit.MILLISECONDS);
|
||||
|
||||
clientSocket.awaitCloseEvent("Client");
|
||||
clientSocket.assertCloseInfo("Client", StatusCode.NORMAL, anything());
|
||||
|
||||
clientSession.close();
|
||||
}
|
||||
|
||||
private void fastFail() throws Exception
|
||||
{
|
||||
client.setMaxIdleTimeout(1000);
|
||||
URI wsUri = server.getServerUri();
|
||||
|
||||
TrackingEndpoint clientSocket = new TrackingEndpoint(testname.getMethodName());
|
||||
ClientUpgradeRequest upgradeRequest = new ClientUpgradeRequest();
|
||||
upgradeRequest.setSubProtocols("fastfail");
|
||||
Future<Session> clientConnectFuture = client.connect(clientSocket, wsUri, upgradeRequest);
|
||||
|
||||
Session clientSession = clientConnectFuture.get(Defaults.CONNECT_TIMEOUT_MS, TimeUnit.MILLISECONDS);
|
||||
|
||||
clientSocket.awaitCloseEvent("Client");
|
||||
clientSocket.assertCloseInfo("Client", StatusCode.SERVER_ERROR, anything());
|
||||
|
||||
clientSession.close();
|
||||
}
|
||||
|
||||
@SuppressWarnings("Duplicates")
|
||||
private void dropConnection() throws Exception
|
||||
{
|
||||
try (IBlockheadClient client = new XBlockheadClient(server.getServerUri()))
|
||||
{
|
||||
client.setProtocols("container");
|
||||
client.setTimeout(1,TimeUnit.SECONDS);
|
||||
try (StacklessLogging ignored = new StacklessLogging(WebSocketSession.class))
|
||||
{
|
||||
client.connect();
|
||||
client.sendStandardRequest();
|
||||
client.expectUpgradeResponse();
|
||||
client.disconnect();
|
||||
}
|
||||
}
|
||||
client.setMaxIdleTimeout(1000);
|
||||
URI wsUri = server.getServerUri();
|
||||
|
||||
TrackingEndpoint clientSocket = new TrackingEndpoint(testname.getMethodName());
|
||||
ClientUpgradeRequest upgradeRequest = new ClientUpgradeRequest();
|
||||
upgradeRequest.setSubProtocols("container");
|
||||
Future<Session> clientConnectFuture = client.connect(clientSocket, wsUri, upgradeRequest);
|
||||
|
||||
Session clientSession = clientConnectFuture.get(Defaults.CONNECT_TIMEOUT_MS, TimeUnit.MILLISECONDS);
|
||||
|
||||
clientSession.close();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,257 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// All rights reserved. This program and the accompanying materials
|
||||
// are made available under the terms of the Eclipse Public License v1.0
|
||||
// and Apache License v2.0 which accompanies this distribution.
|
||||
//
|
||||
// The Eclipse Public License is available at
|
||||
// http://www.eclipse.org/legal/epl-v10.html
|
||||
//
|
||||
// The Apache License v2.0 is available at
|
||||
// http://www.opensource.org/licenses/apache2.0.php
|
||||
//
|
||||
// You may elect to redistribute this code under either of these licenses.
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.websocket.tests.server;
|
||||
|
||||
import static org.hamcrest.Matchers.anything;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.LinkedList;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.eclipse.jetty.util.log.StacklessLogging;
|
||||
import org.eclipse.jetty.websocket.api.Session;
|
||||
import org.eclipse.jetty.websocket.api.StatusCode;
|
||||
import org.eclipse.jetty.websocket.api.WebSocketAdapter;
|
||||
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketClose;
|
||||
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketConnect;
|
||||
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketError;
|
||||
import org.eclipse.jetty.websocket.api.annotations.WebSocket;
|
||||
import org.eclipse.jetty.websocket.client.ClientUpgradeRequest;
|
||||
import org.eclipse.jetty.websocket.client.WebSocketClient;
|
||||
import org.eclipse.jetty.websocket.servlet.ServletUpgradeRequest;
|
||||
import org.eclipse.jetty.websocket.servlet.ServletUpgradeResponse;
|
||||
import org.eclipse.jetty.websocket.servlet.WebSocketCreator;
|
||||
import org.eclipse.jetty.websocket.servlet.WebSocketServlet;
|
||||
import org.eclipse.jetty.websocket.servlet.WebSocketServletFactory;
|
||||
import org.eclipse.jetty.websocket.tests.Defaults;
|
||||
import org.eclipse.jetty.websocket.tests.SimpleServletServer;
|
||||
import org.eclipse.jetty.websocket.tests.TrackingEndpoint;
|
||||
import org.junit.After;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.TestName;
|
||||
|
||||
/**
|
||||
* Testing badly behaving Socket class implementations to get the best
|
||||
* close states and behaviors out of the websocket implementation.
|
||||
*/
|
||||
public class MisbehavingClassTest
|
||||
{
|
||||
@WebSocket
|
||||
public static class AnnotatedRuntimeOnConnectSocket
|
||||
{
|
||||
public LinkedList<Throwable> errors = new LinkedList<>();
|
||||
public CountDownLatch closeLatch = new CountDownLatch(1);
|
||||
public int closeStatusCode;
|
||||
public String closeReason;
|
||||
|
||||
@OnWebSocketConnect
|
||||
public void onWebSocketConnect(Session sess)
|
||||
{
|
||||
// Intentional runtime exception.
|
||||
throw new RuntimeException("Intentional Exception from onWebSocketConnect");
|
||||
}
|
||||
|
||||
@OnWebSocketClose
|
||||
public void onWebSocketClose(int statusCode, String reason)
|
||||
{
|
||||
closeLatch.countDown();
|
||||
closeStatusCode = statusCode;
|
||||
closeReason = reason;
|
||||
}
|
||||
|
||||
@OnWebSocketError
|
||||
public void onWebSocketError(Throwable cause)
|
||||
{
|
||||
this.errors.add(cause);
|
||||
}
|
||||
|
||||
public void reset()
|
||||
{
|
||||
this.closeLatch = new CountDownLatch(1);
|
||||
this.closeStatusCode = -1;
|
||||
this.closeReason = null;
|
||||
this.errors.clear();
|
||||
}
|
||||
}
|
||||
|
||||
public static class ListenerRuntimeOnConnectSocket extends WebSocketAdapter
|
||||
{
|
||||
public LinkedList<Throwable> errors = new LinkedList<>();
|
||||
public CountDownLatch closeLatch = new CountDownLatch(1);
|
||||
public int closeStatusCode;
|
||||
public String closeReason;
|
||||
|
||||
@Override
|
||||
public void onWebSocketConnect(Session sess)
|
||||
{
|
||||
super.onWebSocketConnect(sess);
|
||||
|
||||
throw new RuntimeException("Intentional Exception from onWebSocketConnect");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onWebSocketClose(int statusCode, String reason)
|
||||
{
|
||||
closeLatch.countDown();
|
||||
closeStatusCode = statusCode;
|
||||
closeReason = reason;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onWebSocketError(Throwable cause)
|
||||
{
|
||||
this.errors.add(cause);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onWebSocketText(String message)
|
||||
{
|
||||
getRemote().sendStringByFuture(message);
|
||||
}
|
||||
|
||||
public void reset()
|
||||
{
|
||||
this.closeLatch = new CountDownLatch(1);
|
||||
this.closeStatusCode = -1;
|
||||
this.closeReason = null;
|
||||
this.errors.clear();
|
||||
}
|
||||
}
|
||||
|
||||
public static class BadSocketsServlet extends WebSocketServlet implements WebSocketCreator
|
||||
{
|
||||
public ListenerRuntimeOnConnectSocket listenerRuntimeConnect;
|
||||
public AnnotatedRuntimeOnConnectSocket annotatedRuntimeConnect;
|
||||
|
||||
@Override
|
||||
public void configure(WebSocketServletFactory factory)
|
||||
{
|
||||
factory.setCreator(this);
|
||||
|
||||
this.listenerRuntimeConnect = new ListenerRuntimeOnConnectSocket();
|
||||
this.annotatedRuntimeConnect = new AnnotatedRuntimeOnConnectSocket();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object createWebSocket(ServletUpgradeRequest req, ServletUpgradeResponse resp)
|
||||
{
|
||||
if (req.hasSubProtocol("listener-runtime-connect"))
|
||||
{
|
||||
return this.listenerRuntimeConnect;
|
||||
}
|
||||
else if (req.hasSubProtocol("annotated-runtime-connect"))
|
||||
{
|
||||
return this.annotatedRuntimeConnect;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static SimpleServletServer server;
|
||||
private static BadSocketsServlet badSocketsServlet;
|
||||
|
||||
@BeforeClass
|
||||
public static void startServer() throws Exception
|
||||
{
|
||||
badSocketsServlet = new BadSocketsServlet();
|
||||
server = new SimpleServletServer(badSocketsServlet);
|
||||
server.start();
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void stopServer() throws Exception
|
||||
{
|
||||
server.stop();
|
||||
}
|
||||
|
||||
@Rule
|
||||
public TestName testname = new TestName();
|
||||
|
||||
private WebSocketClient client;
|
||||
|
||||
@Before
|
||||
public void startClient() throws Exception
|
||||
{
|
||||
client = new WebSocketClient();
|
||||
client.start();
|
||||
}
|
||||
|
||||
@After
|
||||
public void stopClient() throws Exception
|
||||
{
|
||||
client.stop();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testListenerRuntimeOnConnect() throws Exception
|
||||
{
|
||||
try (StacklessLogging ignored = new StacklessLogging(ListenerRuntimeOnConnectSocket.class))
|
||||
{
|
||||
ListenerRuntimeOnConnectSocket socket = badSocketsServlet.listenerRuntimeConnect;
|
||||
socket.reset();
|
||||
|
||||
client.setMaxIdleTimeout(TimeUnit.SECONDS.toMillis(1));
|
||||
URI wsUri = server.getServerUri();
|
||||
|
||||
TrackingEndpoint clientSocket = new TrackingEndpoint(testname.getMethodName());
|
||||
ClientUpgradeRequest upgradeRequest = new ClientUpgradeRequest();
|
||||
upgradeRequest.setSubProtocols("listener-runtime-connect");
|
||||
Future<Session> clientConnectFuture = client.connect(clientSocket, wsUri, upgradeRequest);
|
||||
|
||||
Session clientSession = clientConnectFuture.get(Defaults.CONNECT_TIMEOUT_MS, TimeUnit.MILLISECONDS);
|
||||
|
||||
clientSocket.awaitCloseEvent("Client");
|
||||
clientSocket.assertCloseInfo("Client", StatusCode.SERVER_ERROR, anything());
|
||||
|
||||
clientSession.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAnnotatedRuntimeOnConnect() throws Exception
|
||||
{
|
||||
try (StacklessLogging ignored = new StacklessLogging(AnnotatedRuntimeOnConnectSocket.class))
|
||||
{
|
||||
AnnotatedRuntimeOnConnectSocket socket = badSocketsServlet.annotatedRuntimeConnect;
|
||||
socket.reset();
|
||||
|
||||
client.setMaxIdleTimeout(TimeUnit.SECONDS.toMillis(1));
|
||||
URI wsUri = server.getServerUri();
|
||||
|
||||
TrackingEndpoint clientSocket = new TrackingEndpoint(testname.getMethodName());
|
||||
ClientUpgradeRequest upgradeRequest = new ClientUpgradeRequest();
|
||||
upgradeRequest.setSubProtocols("annotated-runtime-connect");
|
||||
Future<Session> clientConnectFuture = client.connect(clientSocket, wsUri, upgradeRequest);
|
||||
|
||||
Session clientSession = clientConnectFuture.get(Defaults.CONNECT_TIMEOUT_MS, TimeUnit.MILLISECONDS);
|
||||
|
||||
clientSocket.awaitCloseEvent("Client");
|
||||
clientSocket.assertCloseInfo("Client", StatusCode.SERVER_ERROR, anything());
|
||||
|
||||
clientSession.close();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,83 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// All rights reserved. This program and the accompanying materials
|
||||
// are made available under the terms of the Eclipse Public License v1.0
|
||||
// and Apache License v2.0 which accompanies this distribution.
|
||||
//
|
||||
// The Eclipse Public License is available at
|
||||
// http://www.eclipse.org/legal/epl-v10.html
|
||||
//
|
||||
// The Apache License v2.0 is available at
|
||||
// http://www.opensource.org/licenses/apache2.0.php
|
||||
//
|
||||
// You may elect to redistribute this code under either of these licenses.
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.websocket.tests.server;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
import org.eclipse.jetty.websocket.api.BatchMode;
|
||||
import org.eclipse.jetty.websocket.api.RemoteEndpoint;
|
||||
import org.eclipse.jetty.websocket.api.Session;
|
||||
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketConnect;
|
||||
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketError;
|
||||
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage;
|
||||
import org.eclipse.jetty.websocket.api.annotations.WebSocket;
|
||||
|
||||
@WebSocket
|
||||
public class RFC6455Socket
|
||||
{
|
||||
private static Logger LOG = Log.getLogger(RFC6455Socket.class);
|
||||
|
||||
private Session session;
|
||||
|
||||
@OnWebSocketMessage
|
||||
public void onBinary(byte buf[], int offset, int len) throws IOException
|
||||
{
|
||||
LOG.debug("onBinary(byte[{}],{},{})",buf.length,offset,len);
|
||||
|
||||
// echo the message back.
|
||||
ByteBuffer data = ByteBuffer.wrap(buf,offset,len);
|
||||
RemoteEndpoint remote = session.getRemote();
|
||||
remote.sendBytes(data, null);
|
||||
if (remote.getBatchMode() == BatchMode.ON)
|
||||
remote.flush();
|
||||
}
|
||||
|
||||
@OnWebSocketConnect
|
||||
public void onOpen(Session sess)
|
||||
{
|
||||
this.session = sess;
|
||||
}
|
||||
|
||||
@OnWebSocketMessage
|
||||
public void onText(String message) throws IOException
|
||||
{
|
||||
LOG.debug("onText({})",message);
|
||||
// Test the RFC 6455 close code 1011 that should close.
|
||||
// trigger a WebSocket server terminated close.
|
||||
if (message.equals("CRASH"))
|
||||
{
|
||||
throw new RuntimeException("Something bad happened");
|
||||
}
|
||||
|
||||
// echo the message back.
|
||||
RemoteEndpoint remote = session.getRemote();
|
||||
remote.sendString(message, null);
|
||||
if (remote.getBatchMode() == BatchMode.ON)
|
||||
remote.flush();
|
||||
}
|
||||
|
||||
@OnWebSocketError
|
||||
public void onError(Throwable cause)
|
||||
{
|
||||
LOG.warn("onError()", cause);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,105 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// All rights reserved. This program and the accompanying materials
|
||||
// are made available under the terms of the Eclipse Public License v1.0
|
||||
// and Apache License v2.0 which accompanies this distribution.
|
||||
//
|
||||
// The Eclipse Public License is available at
|
||||
// http://www.eclipse.org/legal/epl-v10.html
|
||||
//
|
||||
// The Apache License v2.0 is available at
|
||||
// http://www.opensource.org/licenses/apache2.0.php
|
||||
//
|
||||
// You may elect to redistribute this code under either of these licenses.
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.websocket.tests.server;
|
||||
|
||||
import static org.hamcrest.Matchers.anyOf;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.hamcrest.Matchers.notNullValue;
|
||||
|
||||
import java.net.HttpCookie;
|
||||
import java.net.URI;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.eclipse.jetty.websocket.api.Session;
|
||||
import org.eclipse.jetty.websocket.client.ClientUpgradeRequest;
|
||||
import org.eclipse.jetty.websocket.client.WebSocketClient;
|
||||
import org.eclipse.jetty.websocket.tests.Defaults;
|
||||
import org.eclipse.jetty.websocket.tests.SimpleServletServer;
|
||||
import org.eclipse.jetty.websocket.tests.TrackingEndpoint;
|
||||
import org.eclipse.jetty.websocket.tests.servlets.EchoServlet;
|
||||
import org.junit.After;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.TestName;
|
||||
|
||||
public class RequestHeadersTest
|
||||
{
|
||||
private static SimpleServletServer server;
|
||||
|
||||
@BeforeClass
|
||||
public static void startServer() throws Exception
|
||||
{
|
||||
server = new SimpleServletServer(new EchoServlet());
|
||||
server.start();
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void stopServer() throws Exception
|
||||
{
|
||||
server.stop();
|
||||
}
|
||||
|
||||
@Rule
|
||||
public TestName testname = new TestName();
|
||||
|
||||
private WebSocketClient client;
|
||||
|
||||
@Before
|
||||
public void startClient() throws Exception
|
||||
{
|
||||
client = new WebSocketClient();
|
||||
client.start();
|
||||
}
|
||||
|
||||
@After
|
||||
public void stopClient() throws Exception
|
||||
{
|
||||
client.stop();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAccessRequestCookies() throws Exception
|
||||
{
|
||||
URI wsUri = server.getServerUri();
|
||||
client.setMaxIdleTimeout(1000);
|
||||
|
||||
TrackingEndpoint clientSocket = new TrackingEndpoint(testname.getMethodName());
|
||||
ClientUpgradeRequest upgradeRequest = new ClientUpgradeRequest();
|
||||
upgradeRequest.setHeader("Cookie", "fruit=Pear; type=Anjou");
|
||||
Future<Session> clientConnectFuture = client.connect(clientSocket, wsUri, upgradeRequest);
|
||||
|
||||
Session clientSession = clientConnectFuture.get(Defaults.CONNECT_TIMEOUT_MS, TimeUnit.MILLISECONDS);
|
||||
|
||||
List<HttpCookie> cookies = clientSession.getUpgradeRequest().getCookies();
|
||||
Assert.assertThat("Request cookies", cookies, notNullValue());
|
||||
Assert.assertThat("Request cookies.size", cookies.size(), is(2));
|
||||
for (HttpCookie cookie : cookies)
|
||||
{
|
||||
Assert.assertThat("Cookie name", cookie.getName(), anyOf(is("fruit"), is("type")));
|
||||
Assert.assertThat("Cookie value", cookie.getValue(), anyOf(is("Pear"), is("Anjou")));
|
||||
}
|
||||
clientSession.close();
|
||||
}
|
||||
}
|
|
@ -16,29 +16,36 @@
|
|||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.websocket.server;
|
||||
package org.eclipse.jetty.websocket.tests.server;
|
||||
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.eclipse.jetty.toolchain.test.EventQueue;
|
||||
import org.eclipse.jetty.websocket.api.Session;
|
||||
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketConnect;
|
||||
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage;
|
||||
import org.eclipse.jetty.websocket.api.annotations.WebSocket;
|
||||
import org.eclipse.jetty.websocket.common.WebSocketFrame;
|
||||
import org.eclipse.jetty.websocket.common.frames.TextFrame;
|
||||
import org.eclipse.jetty.websocket.common.test.XBlockheadClient;
|
||||
import org.eclipse.jetty.websocket.client.ClientUpgradeRequest;
|
||||
import org.eclipse.jetty.websocket.client.WebSocketClient;
|
||||
import org.eclipse.jetty.websocket.servlet.ServletUpgradeRequest;
|
||||
import org.eclipse.jetty.websocket.servlet.ServletUpgradeResponse;
|
||||
import org.eclipse.jetty.websocket.servlet.WebSocketCreator;
|
||||
import org.eclipse.jetty.websocket.servlet.WebSocketServlet;
|
||||
import org.eclipse.jetty.websocket.servlet.WebSocketServletFactory;
|
||||
import org.eclipse.jetty.websocket.tests.Defaults;
|
||||
import org.eclipse.jetty.websocket.tests.SimpleServletServer;
|
||||
import org.eclipse.jetty.websocket.tests.TrackingEndpoint;
|
||||
import org.junit.After;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.TestName;
|
||||
|
||||
public class SubProtocolTest
|
||||
{
|
||||
|
@ -100,39 +107,60 @@ public class SubProtocolTest
|
|||
}
|
||||
|
||||
@AfterClass
|
||||
public static void stopServer()
|
||||
public static void stopServer() throws Exception
|
||||
{
|
||||
server.stop();
|
||||
}
|
||||
|
||||
@Rule
|
||||
public TestName testname = new TestName();
|
||||
|
||||
private WebSocketClient client;
|
||||
|
||||
@Before
|
||||
public void startClient() throws Exception
|
||||
{
|
||||
client = new WebSocketClient();
|
||||
client.start();
|
||||
}
|
||||
|
||||
@After
|
||||
public void stopClient() throws Exception
|
||||
{
|
||||
client.stop();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSingleProtocol() throws Exception
|
||||
{
|
||||
testSubProtocol("echo", "echo");
|
||||
testSubProtocol(new String[]{"echo"}, "echo");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultipleProtocols() throws Exception
|
||||
{
|
||||
testSubProtocol("chat,info,echo", "chat");
|
||||
testSubProtocol(new String[]{"chat", "info", "echo"}, "chat");
|
||||
}
|
||||
|
||||
private void testSubProtocol(String requestProtocols, String acceptedSubProtocols) throws Exception
|
||||
private void testSubProtocol(String[] requestProtocols, String acceptedSubProtocols) throws Exception
|
||||
{
|
||||
try (XBlockheadClient client = new XBlockheadClient(server.getServerUri()))
|
||||
{
|
||||
client.setTimeout(1, TimeUnit.SECONDS);
|
||||
|
||||
client.connect();
|
||||
client.addHeader("Sec-WebSocket-Protocol: "+ requestProtocols + "\r\n");
|
||||
client.sendStandardRequest();
|
||||
client.expectUpgradeResponse();
|
||||
|
||||
client.write(new TextFrame().setPayload("showme"));
|
||||
EventQueue<WebSocketFrame> frames = client.readFrames(1, 30, TimeUnit.SECONDS);
|
||||
WebSocketFrame tf = frames.poll();
|
||||
|
||||
assertThat(ProtocolEchoSocket.class.getSimpleName() + ".onMessage()", tf.getPayloadAsUTF8(), is("acceptedSubprotocol=" + acceptedSubProtocols));
|
||||
}
|
||||
URI wsUri = server.getServerUri();
|
||||
client.setMaxIdleTimeout(1000);
|
||||
|
||||
TrackingEndpoint clientSocket = new TrackingEndpoint(testname.getMethodName());
|
||||
ClientUpgradeRequest upgradeRequest = new ClientUpgradeRequest();
|
||||
upgradeRequest.setSubProtocols(requestProtocols);
|
||||
Future<Session> clientConnectFuture = client.connect(clientSocket, wsUri, upgradeRequest);
|
||||
|
||||
Session clientSession = clientConnectFuture.get(Defaults.CONNECT_TIMEOUT_MS, TimeUnit.MILLISECONDS);
|
||||
|
||||
// Message
|
||||
clientSession.getRemote().sendString("showme");
|
||||
|
||||
// Read message
|
||||
String incomingMsg = clientSocket.messageQueue.poll(5, TimeUnit.SECONDS);
|
||||
Assert.assertThat("Incoming Message", incomingMsg, is("acceptedSubprotocol=" + acceptedSubProtocols));
|
||||
|
||||
clientSession.close();
|
||||
}
|
||||
}
|
|
@ -16,37 +16,43 @@
|
|||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.websocket.server;
|
||||
package org.eclipse.jetty.websocket.tests.server;
|
||||
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.eclipse.jetty.toolchain.test.EventQueue;
|
||||
import org.eclipse.jetty.websocket.api.Session;
|
||||
import org.eclipse.jetty.websocket.api.SuspendToken;
|
||||
import org.eclipse.jetty.websocket.api.WriteCallback;
|
||||
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketConnect;
|
||||
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage;
|
||||
import org.eclipse.jetty.websocket.api.annotations.WebSocket;
|
||||
import org.eclipse.jetty.websocket.common.WebSocketFrame;
|
||||
import org.eclipse.jetty.websocket.common.frames.TextFrame;
|
||||
import org.eclipse.jetty.websocket.common.test.XBlockheadClient;
|
||||
import org.eclipse.jetty.websocket.client.ClientUpgradeRequest;
|
||||
import org.eclipse.jetty.websocket.client.WebSocketClient;
|
||||
import org.eclipse.jetty.websocket.servlet.ServletUpgradeRequest;
|
||||
import org.eclipse.jetty.websocket.servlet.ServletUpgradeResponse;
|
||||
import org.eclipse.jetty.websocket.servlet.WebSocketCreator;
|
||||
import org.eclipse.jetty.websocket.servlet.WebSocketServlet;
|
||||
import org.eclipse.jetty.websocket.servlet.WebSocketServletFactory;
|
||||
import org.eclipse.jetty.websocket.tests.Defaults;
|
||||
import org.eclipse.jetty.websocket.tests.SimpleServletServer;
|
||||
import org.eclipse.jetty.websocket.tests.TrackingEndpoint;
|
||||
import org.junit.After;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.TestName;
|
||||
|
||||
public class SuspendResumeTest
|
||||
{
|
||||
@WebSocket
|
||||
public static class EchoSocket
|
||||
public static class BackPressureEchoSocket
|
||||
{
|
||||
private Session session;
|
||||
|
||||
|
@ -79,12 +85,12 @@ public class SuspendResumeTest
|
|||
}
|
||||
}
|
||||
|
||||
public static class EchoCreator implements WebSocketCreator
|
||||
public static class BackPressureEchoCreator implements WebSocketCreator
|
||||
{
|
||||
@Override
|
||||
public Object createWebSocket(ServletUpgradeRequest req, ServletUpgradeResponse resp)
|
||||
{
|
||||
return new EchoSocket();
|
||||
return new BackPressureEchoSocket();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -95,7 +101,7 @@ public class SuspendResumeTest
|
|||
@Override
|
||||
public void configure(WebSocketServletFactory factory)
|
||||
{
|
||||
factory.setCreator(new EchoCreator());
|
||||
factory.setCreator(new BackPressureEchoCreator());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -109,30 +115,51 @@ public class SuspendResumeTest
|
|||
}
|
||||
|
||||
@AfterClass
|
||||
public static void stopServer()
|
||||
public static void stopServer() throws Exception
|
||||
{
|
||||
server.stop();
|
||||
}
|
||||
|
||||
@Rule
|
||||
public TestName testname = new TestName();
|
||||
|
||||
private WebSocketClient client;
|
||||
|
||||
@Before
|
||||
public void startClient() throws Exception
|
||||
{
|
||||
client = new WebSocketClient();
|
||||
client.start();
|
||||
}
|
||||
|
||||
@After
|
||||
public void stopClient() throws Exception
|
||||
{
|
||||
client.stop();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSuspendResume() throws Exception
|
||||
{
|
||||
try (XBlockheadClient client = new XBlockheadClient(server.getServerUri()))
|
||||
{
|
||||
client.setTimeout(1, TimeUnit.SECONDS);
|
||||
|
||||
client.connect();
|
||||
client.sendStandardRequest();
|
||||
client.expectUpgradeResponse();
|
||||
|
||||
client.write(new TextFrame().setPayload("echo1"));
|
||||
client.write(new TextFrame().setPayload("echo2"));
|
||||
|
||||
EventQueue<WebSocketFrame> frames = client.readFrames(2, 30, TimeUnit.SECONDS);
|
||||
WebSocketFrame tf = frames.poll();
|
||||
assertThat(EchoSocket.class.getSimpleName() + ".onMessage()", tf.getPayloadAsUTF8(), is("echo1"));
|
||||
tf = frames.poll();
|
||||
assertThat(EchoSocket.class.getSimpleName() + ".onMessage()", tf.getPayloadAsUTF8(), is("echo2"));
|
||||
}
|
||||
URI wsUri = server.getServerUri();
|
||||
|
||||
TrackingEndpoint clientSocket = new TrackingEndpoint(testname.getMethodName());
|
||||
ClientUpgradeRequest upgradeRequest = new ClientUpgradeRequest();
|
||||
Future<Session> clientConnectFuture = client.connect(clientSocket, wsUri, upgradeRequest);
|
||||
|
||||
Session clientSession = clientConnectFuture.get(Defaults.CONNECT_TIMEOUT_MS, TimeUnit.MILLISECONDS);
|
||||
|
||||
// Message
|
||||
clientSession.getRemote().sendString("echo1");
|
||||
clientSession.getRemote().sendString("echo2");
|
||||
|
||||
// Read message
|
||||
String incomingMsg;
|
||||
incomingMsg = clientSocket.messageQueue.poll(5, TimeUnit.SECONDS);
|
||||
Assert.assertThat("Incoming Message 1", incomingMsg, is("echo1"));
|
||||
incomingMsg = clientSocket.messageQueue.poll(5, TimeUnit.SECONDS);
|
||||
Assert.assertThat("Incoming Message 2", incomingMsg, is("echo2"));
|
||||
|
||||
clientSession.close();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,221 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// All rights reserved. This program and the accompanying materials
|
||||
// are made available under the terms of the Eclipse Public License v1.0
|
||||
// and Apache License v2.0 which accompanies this distribution.
|
||||
//
|
||||
// The Eclipse Public License is available at
|
||||
// http://www.eclipse.org/legal/epl-v10.html
|
||||
//
|
||||
// The Apache License v2.0 is available at
|
||||
// http://www.opensource.org/licenses/apache2.0.php
|
||||
//
|
||||
// You may elect to redistribute this code under either of these licenses.
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.websocket.tests.server;
|
||||
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
import java.net.URI;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Arrays;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.eclipse.jetty.websocket.api.Session;
|
||||
import org.eclipse.jetty.websocket.client.ClientUpgradeRequest;
|
||||
import org.eclipse.jetty.websocket.client.WebSocketClient;
|
||||
import org.eclipse.jetty.websocket.tests.Defaults;
|
||||
import org.eclipse.jetty.websocket.tests.LeakTrackingBufferPoolRule;
|
||||
import org.eclipse.jetty.websocket.tests.SimpleServletServer;
|
||||
import org.eclipse.jetty.websocket.tests.TrackingEndpoint;
|
||||
import org.eclipse.jetty.websocket.tests.servlets.EchoServlet;
|
||||
import org.junit.After;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.TestName;
|
||||
|
||||
/**
|
||||
* Test simulating a client that talks too quickly.
|
||||
* <p>
|
||||
* There is a class of client that will send the GET+Upgrade Request along with a few websocket frames in a single
|
||||
* network packet. This test attempts to perform this behavior as close as possible.
|
||||
*/
|
||||
public class TooFastClientTest
|
||||
{
|
||||
private static SimpleServletServer server;
|
||||
|
||||
@BeforeClass
|
||||
public static void startServer() throws Exception
|
||||
{
|
||||
server = new SimpleServletServer(new EchoServlet());
|
||||
server.start();
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void stopServer() throws Exception
|
||||
{
|
||||
server.stop();
|
||||
}
|
||||
|
||||
@Rule
|
||||
LeakTrackingBufferPoolRule bufferPool = new LeakTrackingBufferPoolRule(TooFastClientTest.class.getSimpleName());
|
||||
|
||||
@Rule
|
||||
public TestName testname = new TestName();
|
||||
|
||||
private WebSocketClient client;
|
||||
|
||||
@Before
|
||||
public void startClient() throws Exception
|
||||
{
|
||||
client = new WebSocketClient();
|
||||
client.start();
|
||||
}
|
||||
|
||||
@After
|
||||
public void stopClient() throws Exception
|
||||
{
|
||||
client.stop();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpgradeWithSmallFrames() throws Exception
|
||||
{
|
||||
URI wsUri = server.getServerUri();
|
||||
|
||||
TrackingEndpoint clientSocket = new TrackingEndpoint(testname.getMethodName());
|
||||
ClientUpgradeRequest upgradeRequest = new ClientUpgradeRequest();
|
||||
|
||||
/* TODO
|
||||
Generate the Request ByteBuffer.
|
||||
Complete with ..
|
||||
* A WebSocket Upgrade Request URI
|
||||
* A WebSocket Upgrade Request Headers
|
||||
* A few outgoing WebSocket frames
|
||||
Send this ByteBuffer as the complete HTTP request bytebuffer.
|
||||
|
||||
// Create ByteBuffer representing the initial opening network packet from the client
|
||||
ByteBuffer initialPacket = ByteBuffer.allocate(4096);
|
||||
BufferUtil.clearToFill(initialPacket);
|
||||
|
||||
// Add upgrade request to packet
|
||||
StringBuilder upgradeRequest = client.generateUpgradeRequest();
|
||||
ByteBuffer upgradeBuffer = BufferUtil.toBuffer(upgradeRequest.toString(), StandardCharsets.UTF_8);
|
||||
initialPacket.put(upgradeBuffer);
|
||||
|
||||
// Add text frames
|
||||
Generator generator = new Generator(WebSocketPolicy.newClientPolicy(), bufferPool);
|
||||
|
||||
String msg1 = "Echo 1";
|
||||
String msg2 = "This is also an echooooo!";
|
||||
|
||||
TextFrame frame1 = new TextFrame().setPayload(msg1);
|
||||
TextFrame frame2 = new TextFrame().setPayload(msg2);
|
||||
|
||||
// Need to set frame mask (as these are client frames)
|
||||
byte mask[] = new byte[]{0x11, 0x22, 0x33, 0x44};
|
||||
frame1.setMask(mask);
|
||||
frame2.setMask(mask);
|
||||
|
||||
generator.generateWholeFrame(frame1, initialPacket);
|
||||
generator.generateWholeFrame(frame2, initialPacket);
|
||||
|
||||
// Write packet to network
|
||||
BufferUtil.flipToFlush(initialPacket, 0);
|
||||
client.writeRaw(initialPacket);
|
||||
*/
|
||||
|
||||
Future<Session> clientConnectFuture = client.connect(clientSocket, wsUri, upgradeRequest);
|
||||
|
||||
// Expect upgrade success
|
||||
Session clientSession = clientConnectFuture.get(Defaults.CONNECT_TIMEOUT_MS, TimeUnit.MILLISECONDS);
|
||||
|
||||
// Read incoming messages
|
||||
String incomingMessage;
|
||||
incomingMessage = clientSocket.messageQueue.poll(5, TimeUnit.SECONDS);
|
||||
assertThat("Echoed Incoming Message 1", incomingMessage, is("Echo 1"));
|
||||
incomingMessage = clientSocket.messageQueue.poll(5, TimeUnit.SECONDS);
|
||||
assertThat("Echoed Incoming Message 2", incomingMessage, is("This is also an echooooo!"));
|
||||
|
||||
clientSession.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test where were a client sends a HTTP Upgrade to websocket AND enough websocket frame(s)
|
||||
* to completely overfill the {@link org.eclipse.jetty.io.AbstractConnection#getInputBufferSize()}
|
||||
* to test a situation where the WebSocket connection opens with prefill that exceeds
|
||||
* the normal input buffer sizes.
|
||||
*
|
||||
* @throws Exception on test failure
|
||||
*/
|
||||
@Test
|
||||
public void testUpgradeWithLargeFrame() throws Exception
|
||||
{
|
||||
URI wsUri = server.getServerUri();
|
||||
|
||||
TrackingEndpoint clientSocket = new TrackingEndpoint(testname.getMethodName());
|
||||
ClientUpgradeRequest upgradeRequest = new ClientUpgradeRequest();
|
||||
|
||||
byte bigMsgBytes[] = new byte[64 * 1024];
|
||||
Arrays.fill(bigMsgBytes, (byte) 'x');
|
||||
String bigMsg = new String(bigMsgBytes, StandardCharsets.UTF_8);
|
||||
|
||||
/* TODO
|
||||
Generate the Request ByteBuffer.
|
||||
Complete with ..
|
||||
* A WebSocket Upgrade Request URI
|
||||
* A WebSocket Upgrade Request Headers
|
||||
* A big enough outgoing WebSocket frame
|
||||
that will trigger a prefill + an unread buffer
|
||||
Send this ByteBuffer as the complete HTTP request bytebuffer.
|
||||
|
||||
// Create ByteBuffer representing the initial opening network packet from the client
|
||||
ByteBuffer initialPacket = ByteBuffer.allocate(100 * 1024);
|
||||
BufferUtil.clearToFill(initialPacket);
|
||||
|
||||
// Add upgrade request to packet
|
||||
StringBuilder upgradeRequest = client.generateUpgradeRequest();
|
||||
ByteBuffer upgradeBuffer = BufferUtil.toBuffer(upgradeRequest.toString(), StandardCharsets.UTF_8);
|
||||
initialPacket.put(upgradeBuffer);
|
||||
|
||||
// Add text frames
|
||||
Generator generator = new Generator(WebSocketPolicy.newClientPolicy(), bufferPool);
|
||||
|
||||
// Need to set frame mask (as these are client frames)
|
||||
byte mask[] = new byte[]{0x11, 0x22, 0x33, 0x44};
|
||||
TextFrame frame = new TextFrame().setPayload(bigMsg);
|
||||
frame.setMask(mask);
|
||||
generator.generateWholeFrame(frame, initialPacket);
|
||||
|
||||
// Write packet to network
|
||||
BufferUtil.flipToFlush(initialPacket, 0);
|
||||
client.writeRaw(initialPacket);
|
||||
|
||||
// Expect upgrade
|
||||
client.expectUpgradeResponse();
|
||||
*/
|
||||
|
||||
Future<Session> clientConnectFuture = client.connect(clientSocket, wsUri, upgradeRequest);
|
||||
|
||||
// Expect upgrade success
|
||||
Session clientSession = clientConnectFuture.get(Defaults.CONNECT_TIMEOUT_MS, TimeUnit.MILLISECONDS);
|
||||
|
||||
// Read incoming messages
|
||||
String incomingMessage;
|
||||
incomingMessage = clientSocket.messageQueue.poll(5, TimeUnit.SECONDS);
|
||||
assertThat("Echoed Incoming Message 1", incomingMessage, is(bigMsg));
|
||||
|
||||
clientSession.close();
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -16,41 +16,44 @@
|
|||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.websocket.server;
|
||||
package org.eclipse.jetty.websocket.tests.server;
|
||||
|
||||
import static org.hamcrest.Matchers.anything;
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.eclipse.jetty.toolchain.test.EventQueue;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
import org.eclipse.jetty.util.log.StacklessLogging;
|
||||
import org.eclipse.jetty.websocket.api.Session;
|
||||
import org.eclipse.jetty.websocket.api.StatusCode;
|
||||
import org.eclipse.jetty.websocket.api.WebSocketAdapter;
|
||||
import org.eclipse.jetty.websocket.common.CloseInfo;
|
||||
import org.eclipse.jetty.websocket.common.OpCode;
|
||||
import org.eclipse.jetty.websocket.common.WebSocketFrame;
|
||||
import org.eclipse.jetty.websocket.client.ClientUpgradeRequest;
|
||||
import org.eclipse.jetty.websocket.client.WebSocketClient;
|
||||
import org.eclipse.jetty.websocket.common.WebSocketSession;
|
||||
import org.eclipse.jetty.websocket.common.frames.TextFrame;
|
||||
import org.eclipse.jetty.websocket.common.test.XBlockheadClient;
|
||||
import org.eclipse.jetty.websocket.common.test.IBlockheadClient;
|
||||
import org.eclipse.jetty.websocket.server.helper.RFCSocket;
|
||||
import org.eclipse.jetty.websocket.server.WebSocketServerFactory;
|
||||
import org.eclipse.jetty.websocket.servlet.ServletUpgradeRequest;
|
||||
import org.eclipse.jetty.websocket.servlet.ServletUpgradeResponse;
|
||||
import org.eclipse.jetty.websocket.servlet.WebSocketCreator;
|
||||
import org.eclipse.jetty.websocket.servlet.WebSocketServlet;
|
||||
import org.eclipse.jetty.websocket.servlet.WebSocketServletFactory;
|
||||
import org.eclipse.jetty.websocket.tests.Defaults;
|
||||
import org.eclipse.jetty.websocket.tests.SimpleServletServer;
|
||||
import org.eclipse.jetty.websocket.tests.TrackingEndpoint;
|
||||
import org.junit.After;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.TestName;
|
||||
|
||||
/**
|
||||
* Tests various close scenarios
|
||||
|
@ -115,7 +118,7 @@ public class WebSocketCloseTest
|
|||
closeSocket = new ContainerSocket(serverFactory);
|
||||
return closeSocket;
|
||||
}
|
||||
return new RFCSocket();
|
||||
return new RFC6455Socket();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -206,10 +209,28 @@ public class WebSocketCloseTest
|
|||
}
|
||||
|
||||
@AfterClass
|
||||
public static void stopServer()
|
||||
public static void stopServer() throws Exception
|
||||
{
|
||||
server.stop();
|
||||
}
|
||||
|
||||
@Rule
|
||||
public TestName testname = new TestName();
|
||||
|
||||
private WebSocketClient client;
|
||||
|
||||
@Before
|
||||
public void startClient() throws Exception
|
||||
{
|
||||
client = new WebSocketClient();
|
||||
client.start();
|
||||
}
|
||||
|
||||
@After
|
||||
public void stopClient() throws Exception
|
||||
{
|
||||
client.stop();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test fast close (bug #403817)
|
||||
|
@ -220,28 +241,20 @@ public class WebSocketCloseTest
|
|||
@Test
|
||||
public void testFastClose() throws Exception
|
||||
{
|
||||
try (IBlockheadClient client = new XBlockheadClient(server.getServerUri()))
|
||||
{
|
||||
client.setProtocols("fastclose");
|
||||
client.setTimeout(5,TimeUnit.SECONDS);
|
||||
client.connect();
|
||||
client.sendStandardRequest();
|
||||
client.expectUpgradeResponse();
|
||||
client.setMaxIdleTimeout(5000);
|
||||
URI wsUri = server.getServerUri();
|
||||
|
||||
TrackingEndpoint clientSocket = new TrackingEndpoint(testname.getMethodName());
|
||||
ClientUpgradeRequest upgradeRequest = new ClientUpgradeRequest();
|
||||
upgradeRequest.setSubProtocols("fastclose");
|
||||
Future<Session> clientConnectFuture = client.connect(clientSocket, wsUri, upgradeRequest);
|
||||
|
||||
Session clientSession = clientConnectFuture.get(Defaults.CONNECT_TIMEOUT_MS, TimeUnit.MILLISECONDS);
|
||||
|
||||
// Verify that client got close frame
|
||||
EventQueue<WebSocketFrame> frames = client.readFrames(1,5,TimeUnit.SECONDS);
|
||||
WebSocketFrame frame = frames.poll();
|
||||
assertThat("frames[0].opcode",frame.getOpCode(),is(OpCode.CLOSE));
|
||||
CloseInfo close = new CloseInfo(frame);
|
||||
assertThat("Close Status Code",close.getStatusCode(),is(StatusCode.NORMAL));
|
||||
|
||||
// Notify server of close handshake
|
||||
client.write(close.asFrame()); // respond with close
|
||||
|
||||
// ensure server socket got close event
|
||||
assertThat("Fast Close Latch",closeSocket.closeLatch.await(5,TimeUnit.SECONDS),is(true));
|
||||
assertThat("Fast Close.statusCode",closeSocket.closeStatusCode,is(StatusCode.NORMAL));
|
||||
}
|
||||
clientSocket.awaitCloseEvent("Client");
|
||||
clientSocket.assertCloseInfo("Client", StatusCode.NORMAL, anything());
|
||||
|
||||
clientSession.close();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -253,30 +266,20 @@ public class WebSocketCloseTest
|
|||
@Test
|
||||
public void testFastFail() throws Exception
|
||||
{
|
||||
try (IBlockheadClient client = new XBlockheadClient(server.getServerUri()))
|
||||
{
|
||||
client.setProtocols("fastfail");
|
||||
client.setTimeout(1,TimeUnit.SECONDS);
|
||||
try (StacklessLogging ignored = new StacklessLogging(CloseServlet.class))
|
||||
{
|
||||
client.connect();
|
||||
client.sendStandardRequest();
|
||||
client.expectUpgradeResponse();
|
||||
|
||||
EventQueue<WebSocketFrame> frames = client.readFrames(1,5,TimeUnit.SECONDS);
|
||||
WebSocketFrame frame = frames.poll();
|
||||
assertThat("frames[0].opcode",frame.getOpCode(),is(OpCode.CLOSE));
|
||||
CloseInfo close = new CloseInfo(frame);
|
||||
assertThat("Close Status Code",close.getStatusCode(),is(StatusCode.SERVER_ERROR));
|
||||
|
||||
client.write(close.asFrame()); // respond with close
|
||||
|
||||
// ensure server socket got close event
|
||||
assertThat("Fast Fail Latch",closeSocket.closeLatch.await(5,TimeUnit.SECONDS),is(true));
|
||||
assertThat("Fast Fail.statusCode",closeSocket.closeStatusCode,is(StatusCode.SERVER_ERROR));
|
||||
assertThat("Fast Fail.errors",closeSocket.errors.size(),is(1));
|
||||
}
|
||||
}
|
||||
client.setMaxIdleTimeout(1000);
|
||||
URI wsUri = server.getServerUri();
|
||||
|
||||
TrackingEndpoint clientSocket = new TrackingEndpoint(testname.getMethodName());
|
||||
ClientUpgradeRequest upgradeRequest = new ClientUpgradeRequest();
|
||||
upgradeRequest.setSubProtocols("fastfail");
|
||||
Future<Session> clientConnectFuture = client.connect(clientSocket, wsUri, upgradeRequest);
|
||||
|
||||
Session clientSession = clientConnectFuture.get(Defaults.CONNECT_TIMEOUT_MS, TimeUnit.MILLISECONDS);
|
||||
|
||||
clientSocket.awaitCloseEvent("Client");
|
||||
clientSocket.assertCloseInfo("Client", StatusCode.SERVER_ERROR, anything());
|
||||
|
||||
clientSession.close();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -291,106 +294,76 @@ public class WebSocketCloseTest
|
|||
fastFail();
|
||||
fastClose();
|
||||
dropConnection();
|
||||
|
||||
client.setMaxIdleTimeout(1000);
|
||||
URI wsUri = server.getServerUri();
|
||||
|
||||
TrackingEndpoint clientSocket = new TrackingEndpoint(testname.getMethodName());
|
||||
ClientUpgradeRequest upgradeRequest = new ClientUpgradeRequest();
|
||||
upgradeRequest.setSubProtocols("container");
|
||||
Future<Session> clientConnectFuture = client.connect(clientSocket, wsUri, upgradeRequest);
|
||||
|
||||
Session clientSession = clientConnectFuture.get(Defaults.CONNECT_TIMEOUT_MS, TimeUnit.MILLISECONDS);
|
||||
|
||||
clientSession.getRemote().sendString("openSessions");
|
||||
|
||||
String incomingMessage = clientSocket.messageQueue.poll(5, TimeUnit.SECONDS);
|
||||
assertThat("Incoming Message", incomingMessage, containsString("openSessions.size=1\n"));
|
||||
|
||||
try (IBlockheadClient client = new XBlockheadClient(server.getServerUri()))
|
||||
{
|
||||
client.setProtocols("container");
|
||||
client.setTimeout(1,TimeUnit.SECONDS);
|
||||
client.connect();
|
||||
client.sendStandardRequest();
|
||||
client.expectUpgradeResponse();
|
||||
|
||||
TextFrame text = new TextFrame();
|
||||
text.setPayload("openSessions");
|
||||
client.write(text);
|
||||
|
||||
EventQueue<WebSocketFrame> frames = client.readFrames(2,1,TimeUnit.SECONDS);
|
||||
WebSocketFrame frame = frames.poll();
|
||||
assertThat("frames[0].opcode",frame.getOpCode(),is(OpCode.TEXT));
|
||||
|
||||
String resp = frame.getPayloadAsUTF8();
|
||||
assertThat("Should only have 1 open session",resp,containsString("openSessions.size=1\n"));
|
||||
|
||||
frame = frames.poll();
|
||||
assertThat("frames[1].opcode",frame.getOpCode(),is(OpCode.CLOSE));
|
||||
CloseInfo close = new CloseInfo(frame);
|
||||
assertThat("Close Status Code",close.getStatusCode(),is(StatusCode.NORMAL));
|
||||
client.write(close.asFrame()); // respond with close
|
||||
|
||||
// ensure server socket got close event
|
||||
assertThat("Open Sessions Latch",closeSocket.closeLatch.await(1,TimeUnit.SECONDS),is(true));
|
||||
assertThat("Open Sessions.statusCode",closeSocket.closeStatusCode,is(StatusCode.NORMAL));
|
||||
assertThat("Open Sessions.errors",closeSocket.errors.size(),is(0));
|
||||
}
|
||||
clientSocket.awaitCloseEvent("Client");
|
||||
clientSocket.assertCloseInfo("Client", StatusCode.NORMAL, anything());
|
||||
}
|
||||
|
||||
@SuppressWarnings("Duplicates")
|
||||
private void fastClose() throws Exception
|
||||
{
|
||||
try (IBlockheadClient client = new XBlockheadClient(server.getServerUri()))
|
||||
{
|
||||
client.setProtocols("fastclose");
|
||||
client.setTimeout(1,TimeUnit.SECONDS);
|
||||
try (StacklessLogging ignored = new StacklessLogging(WebSocketSession.class))
|
||||
{
|
||||
client.connect();
|
||||
client.sendStandardRequest();
|
||||
client.expectUpgradeResponse();
|
||||
|
||||
client.readFrames(1,1,TimeUnit.SECONDS);
|
||||
|
||||
CloseInfo close = new CloseInfo(StatusCode.NORMAL,"Normal");
|
||||
assertThat("Close Status Code",close.getStatusCode(),is(StatusCode.NORMAL));
|
||||
|
||||
// Notify server of close handshake
|
||||
client.write(close.asFrame()); // respond with close
|
||||
|
||||
// ensure server socket got close event
|
||||
assertThat("Fast Close Latch",closeSocket.closeLatch.await(1,TimeUnit.SECONDS),is(true));
|
||||
assertThat("Fast Close.statusCode",closeSocket.closeStatusCode,is(StatusCode.NORMAL));
|
||||
}
|
||||
}
|
||||
client.setMaxIdleTimeout(1000);
|
||||
URI wsUri = server.getServerUri();
|
||||
|
||||
TrackingEndpoint clientSocket = new TrackingEndpoint(testname.getMethodName());
|
||||
ClientUpgradeRequest upgradeRequest = new ClientUpgradeRequest();
|
||||
upgradeRequest.setSubProtocols("fastclose");
|
||||
Future<Session> clientConnectFuture = client.connect(clientSocket, wsUri, upgradeRequest);
|
||||
|
||||
Session clientSession = clientConnectFuture.get(Defaults.CONNECT_TIMEOUT_MS, TimeUnit.MILLISECONDS);
|
||||
|
||||
clientSocket.awaitCloseEvent("Client");
|
||||
clientSocket.assertCloseInfo("Client", StatusCode.NORMAL, anything());
|
||||
|
||||
clientSession.close();
|
||||
}
|
||||
|
||||
private void fastFail() throws Exception
|
||||
{
|
||||
try (IBlockheadClient client = new XBlockheadClient(server.getServerUri()))
|
||||
{
|
||||
client.setProtocols("fastfail");
|
||||
client.setTimeout(1,TimeUnit.SECONDS);
|
||||
try (StacklessLogging ignored = new StacklessLogging(WebSocketSession.class))
|
||||
{
|
||||
client.connect();
|
||||
client.sendStandardRequest();
|
||||
client.expectUpgradeResponse();
|
||||
|
||||
client.readFrames(1,1,TimeUnit.SECONDS);
|
||||
|
||||
CloseInfo close = new CloseInfo(StatusCode.NORMAL,"Normal");
|
||||
client.write(close.asFrame()); // respond with close
|
||||
|
||||
// ensure server socket got close event
|
||||
assertThat("Fast Fail Latch",closeSocket.closeLatch.await(1,TimeUnit.SECONDS),is(true));
|
||||
assertThat("Fast Fail.statusCode",closeSocket.closeStatusCode,is(StatusCode.SERVER_ERROR));
|
||||
assertThat("Fast Fail.errors",closeSocket.errors.size(),is(1));
|
||||
}
|
||||
}
|
||||
client.setMaxIdleTimeout(1000);
|
||||
URI wsUri = server.getServerUri();
|
||||
|
||||
TrackingEndpoint clientSocket = new TrackingEndpoint(testname.getMethodName());
|
||||
ClientUpgradeRequest upgradeRequest = new ClientUpgradeRequest();
|
||||
upgradeRequest.setSubProtocols("fastfail");
|
||||
Future<Session> clientConnectFuture = client.connect(clientSocket, wsUri, upgradeRequest);
|
||||
|
||||
Session clientSession = clientConnectFuture.get(Defaults.CONNECT_TIMEOUT_MS, TimeUnit.MILLISECONDS);
|
||||
|
||||
clientSocket.awaitCloseEvent("Client");
|
||||
clientSocket.assertCloseInfo("Client", StatusCode.SERVER_ERROR, anything());
|
||||
|
||||
clientSession.close();
|
||||
}
|
||||
|
||||
@SuppressWarnings("Duplicates")
|
||||
private void dropConnection() throws Exception
|
||||
{
|
||||
try (IBlockheadClient client = new XBlockheadClient(server.getServerUri()))
|
||||
{
|
||||
client.setProtocols("container");
|
||||
client.setTimeout(1,TimeUnit.SECONDS);
|
||||
try (StacklessLogging ignored = new StacklessLogging(WebSocketSession.class))
|
||||
{
|
||||
client.connect();
|
||||
client.sendStandardRequest();
|
||||
client.expectUpgradeResponse();
|
||||
client.disconnect();
|
||||
}
|
||||
}
|
||||
client.setMaxIdleTimeout(1000);
|
||||
URI wsUri = server.getServerUri();
|
||||
|
||||
TrackingEndpoint clientSocket = new TrackingEndpoint(testname.getMethodName());
|
||||
ClientUpgradeRequest upgradeRequest = new ClientUpgradeRequest();
|
||||
upgradeRequest.setSubProtocols("container");
|
||||
Future<Session> clientConnectFuture = client.connect(clientSocket, wsUri, upgradeRequest);
|
||||
|
||||
Session clientSession = clientConnectFuture.get(Defaults.CONNECT_TIMEOUT_MS, TimeUnit.MILLISECONDS);
|
||||
|
||||
clientSession.close();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,90 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// All rights reserved. This program and the accompanying materials
|
||||
// are made available under the terms of the Eclipse Public License v1.0
|
||||
// and Apache License v2.0 which accompanies this distribution.
|
||||
//
|
||||
// The Eclipse Public License is available at
|
||||
// http://www.eclipse.org/legal/epl-v10.html
|
||||
//
|
||||
// The Apache License v2.0 is available at
|
||||
// http://www.opensource.org/licenses/apache2.0.php
|
||||
//
|
||||
// You may elect to redistribute this code under either of these licenses.
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.websocket.tests.server;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
import org.eclipse.jetty.websocket.api.Session;
|
||||
import org.eclipse.jetty.websocket.client.ClientUpgradeRequest;
|
||||
import org.eclipse.jetty.websocket.client.WebSocketClient;
|
||||
import org.eclipse.jetty.websocket.tests.SimpleServletServer;
|
||||
import org.eclipse.jetty.websocket.tests.TrackingEndpoint;
|
||||
import org.eclipse.jetty.websocket.tests.servlets.EchoServlet;
|
||||
import org.junit.After;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.TestName;
|
||||
|
||||
public class WebSocketInvalidVersionTest
|
||||
{
|
||||
private static SimpleServletServer server;
|
||||
|
||||
@BeforeClass
|
||||
public static void startServer() throws Exception
|
||||
{
|
||||
server = new SimpleServletServer(new EchoServlet());
|
||||
server.start();
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void stopServer() throws Exception
|
||||
{
|
||||
server.stop();
|
||||
}
|
||||
|
||||
@Rule
|
||||
public TestName testname = new TestName();
|
||||
|
||||
private WebSocketClient client;
|
||||
|
||||
@Before
|
||||
public void startClient() throws Exception
|
||||
{
|
||||
client = new WebSocketClient();
|
||||
client.start();
|
||||
}
|
||||
|
||||
@After
|
||||
public void stopClient() throws Exception
|
||||
{
|
||||
client.stop();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the requirement of responding with an http 400 when using a Sec-WebSocket-Version that is unsupported.
|
||||
* @throws Exception on test failure
|
||||
*/
|
||||
@Test
|
||||
public void testRequestVersion29() throws Exception
|
||||
{
|
||||
URI wsUri = server.getServerUri();
|
||||
|
||||
TrackingEndpoint clientSocket = new TrackingEndpoint(testname.getMethodName());
|
||||
ClientUpgradeRequest upgradeRequest = new ClientUpgradeRequest();
|
||||
upgradeRequest.setHeader("Sec-WebSocket-Version", "29");
|
||||
Future<Session> clientConnectFuture = client.connect(clientSocket, wsUri, upgradeRequest);
|
||||
// TODO: handle exception?
|
||||
Assert.fail("Should have handled exception check");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,238 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// All rights reserved. This program and the accompanying materials
|
||||
// are made available under the terms of the Eclipse Public License v1.0
|
||||
// and Apache License v2.0 which accompanies this distribution.
|
||||
//
|
||||
// The Eclipse Public License is available at
|
||||
// http://www.eclipse.org/legal/epl-v10.html
|
||||
//
|
||||
// The Apache License v2.0 is available at
|
||||
// http://www.opensource.org/licenses/apache2.0.php
|
||||
//
|
||||
// You may elect to redistribute this code under either of these licenses.
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.websocket.tests.server;
|
||||
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.eclipse.jetty.toolchain.test.AdvancedRunner;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
import org.eclipse.jetty.websocket.api.BatchMode;
|
||||
import org.eclipse.jetty.websocket.api.RemoteEndpoint;
|
||||
import org.eclipse.jetty.websocket.api.Session;
|
||||
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketConnect;
|
||||
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage;
|
||||
import org.eclipse.jetty.websocket.api.annotations.WebSocket;
|
||||
import org.eclipse.jetty.websocket.client.ClientUpgradeRequest;
|
||||
import org.eclipse.jetty.websocket.client.WebSocketClient;
|
||||
import org.eclipse.jetty.websocket.servlet.WebSocketServlet;
|
||||
import org.eclipse.jetty.websocket.servlet.WebSocketServletFactory;
|
||||
import org.eclipse.jetty.websocket.tests.Defaults;
|
||||
import org.eclipse.jetty.websocket.tests.SimpleServletServer;
|
||||
import org.eclipse.jetty.websocket.tests.TrackingEndpoint;
|
||||
import org.junit.After;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.TestName;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
/**
|
||||
* Testing various aspects of the server side support for WebSocket {@link org.eclipse.jetty.websocket.api.Session}
|
||||
*/
|
||||
@RunWith(AdvancedRunner.class)
|
||||
public class WebSocketServerSessionTest
|
||||
{
|
||||
public static class SessionServlet extends WebSocketServlet
|
||||
{
|
||||
@Override
|
||||
public void configure(WebSocketServletFactory factory)
|
||||
{
|
||||
factory.register(SessionSocket.class);
|
||||
}
|
||||
}
|
||||
|
||||
@WebSocket
|
||||
public static class SessionSocket
|
||||
{
|
||||
private static final Logger LOG = Log.getLogger(SessionSocket.class);
|
||||
private Session session;
|
||||
|
||||
@OnWebSocketConnect
|
||||
public void onConnect(Session sess)
|
||||
{
|
||||
this.session = sess;
|
||||
}
|
||||
|
||||
@OnWebSocketMessage
|
||||
public void onText(String message)
|
||||
{
|
||||
LOG.debug("onText({})",message);
|
||||
if (message == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
if (message.startsWith("getParameterMap"))
|
||||
{
|
||||
Map<String, List<String>> parameterMap = session.getUpgradeRequest().getParameterMap();
|
||||
|
||||
int idx = message.indexOf('|');
|
||||
String key = message.substring(idx + 1);
|
||||
List<String> values = parameterMap.get(key);
|
||||
|
||||
if (values == null)
|
||||
{
|
||||
sendString("<null>");
|
||||
return;
|
||||
}
|
||||
|
||||
StringBuilder valueStr = new StringBuilder();
|
||||
valueStr.append('[');
|
||||
boolean delim = false;
|
||||
for (String value : values)
|
||||
{
|
||||
if (delim)
|
||||
{
|
||||
valueStr.append(", ");
|
||||
}
|
||||
valueStr.append(value);
|
||||
delim = true;
|
||||
}
|
||||
valueStr.append(']');
|
||||
LOG.debug("valueStr = {}", valueStr);
|
||||
sendString(valueStr.toString());
|
||||
return;
|
||||
}
|
||||
|
||||
if ("session.isSecure".equals(message))
|
||||
{
|
||||
String issecure = String.format("session.isSecure=%b",session.isSecure());
|
||||
sendString(issecure);
|
||||
return;
|
||||
}
|
||||
|
||||
if ("session.upgradeRequest.requestURI".equals(message))
|
||||
{
|
||||
String response = String.format("session.upgradeRequest.requestURI=%s",session.getUpgradeRequest().getRequestURI().toASCIIString());
|
||||
sendString(response);
|
||||
return;
|
||||
}
|
||||
|
||||
if ("harsh-disconnect".equals(message))
|
||||
{
|
||||
session.disconnect();
|
||||
return;
|
||||
}
|
||||
|
||||
// echo the message back.
|
||||
sendString(message);
|
||||
}
|
||||
catch (Throwable t)
|
||||
{
|
||||
LOG.warn(t);
|
||||
}
|
||||
}
|
||||
|
||||
protected void sendString(String text) throws IOException
|
||||
{
|
||||
RemoteEndpoint remote = session.getRemote();
|
||||
remote.sendString(text, null);
|
||||
if (remote.getBatchMode() == BatchMode.ON)
|
||||
remote.flush();
|
||||
}
|
||||
}
|
||||
|
||||
private static SimpleServletServer server;
|
||||
|
||||
@BeforeClass
|
||||
public static void startServer() throws Exception
|
||||
{
|
||||
server = new SimpleServletServer(new SessionServlet());
|
||||
server.start();
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void stopServer() throws Exception
|
||||
{
|
||||
server.stop();
|
||||
}
|
||||
|
||||
@Rule
|
||||
public TestName testname = new TestName();
|
||||
|
||||
private WebSocketClient client;
|
||||
|
||||
@Before
|
||||
public void startClient() throws Exception
|
||||
{
|
||||
client = new WebSocketClient();
|
||||
client.start();
|
||||
}
|
||||
|
||||
@After
|
||||
public void stopClient() throws Exception
|
||||
{
|
||||
client.stop();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDisconnect() throws Exception
|
||||
{
|
||||
URI wsUri = server.getServerUri().resolve("/test/disconnect");
|
||||
|
||||
TrackingEndpoint clientSocket = new TrackingEndpoint(testname.getMethodName());
|
||||
ClientUpgradeRequest upgradeRequest = new ClientUpgradeRequest();
|
||||
Future<Session> clientConnectFuture = client.connect(clientSocket, wsUri, upgradeRequest);
|
||||
|
||||
Session clientSession = clientConnectFuture.get(Defaults.CONNECT_TIMEOUT_MS, TimeUnit.MILLISECONDS);
|
||||
clientSession.getRemote().sendString("harsh-disconnect");
|
||||
|
||||
// TODO: or onError(EOF)
|
||||
clientSocket.awaitCloseEvent("Client");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpgradeRequestResponse() throws Exception
|
||||
{
|
||||
URI wsUri = server.getServerUri().resolve("/test?snack=cashews&amount=handful&brand=off");
|
||||
|
||||
TrackingEndpoint clientSocket = new TrackingEndpoint(testname.getMethodName());
|
||||
ClientUpgradeRequest upgradeRequest = new ClientUpgradeRequest();
|
||||
Future<Session> clientConnectFuture = client.connect(clientSocket, wsUri, upgradeRequest);
|
||||
|
||||
Session clientSession = clientConnectFuture.get(Defaults.CONNECT_TIMEOUT_MS, TimeUnit.MILLISECONDS);
|
||||
clientSession.getRemote().sendString("getParameterMap|snack");
|
||||
clientSession.getRemote().sendString("getParameterMap|amount");
|
||||
clientSession.getRemote().sendString("getParameterMap|brand");
|
||||
clientSession.getRemote().sendString("getParameterMap|cost");
|
||||
|
||||
String incomingMessage;
|
||||
incomingMessage = clientSocket.messageQueue.poll(5, TimeUnit.SECONDS);
|
||||
assertThat("Parameter Map[snack]", incomingMessage, is("[cashews]"));
|
||||
incomingMessage = clientSocket.messageQueue.poll(5, TimeUnit.SECONDS);
|
||||
assertThat("Parameter Map[amount]", incomingMessage, is("[handful]"));
|
||||
incomingMessage = clientSocket.messageQueue.poll(5, TimeUnit.SECONDS);
|
||||
assertThat("Parameter Map[brand]", incomingMessage, is("[off]"));
|
||||
incomingMessage = clientSocket.messageQueue.poll(5, TimeUnit.SECONDS);
|
||||
assertThat("Parameter Map[cost]", incomingMessage, is("<null>"));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,328 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// All rights reserved. This program and the accompanying materials
|
||||
// are made available under the terms of the Eclipse Public License v1.0
|
||||
// and Apache License v2.0 which accompanies this distribution.
|
||||
//
|
||||
// The Eclipse Public License is available at
|
||||
// http://www.eclipse.org/legal/epl-v10.html
|
||||
//
|
||||
// The Apache License v2.0 is available at
|
||||
// http://www.opensource.org/licenses/apache2.0.php
|
||||
//
|
||||
// You may elect to redistribute this code under either of these licenses.
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.websocket.tests.server;
|
||||
|
||||
import static org.hamcrest.Matchers.anything;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
import java.net.URI;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Arrays;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.eclipse.jetty.toolchain.test.AdvancedRunner;
|
||||
import org.eclipse.jetty.toolchain.test.Hex;
|
||||
import org.eclipse.jetty.util.Utf8Appendable.NotUtf8Exception;
|
||||
import org.eclipse.jetty.util.Utf8StringBuilder;
|
||||
import org.eclipse.jetty.util.log.StacklessLogging;
|
||||
import org.eclipse.jetty.util.log.StdErrLog;
|
||||
import org.eclipse.jetty.websocket.api.StatusCode;
|
||||
import org.eclipse.jetty.websocket.api.WebSocketPolicy;
|
||||
import org.eclipse.jetty.websocket.client.ClientUpgradeRequest;
|
||||
import org.eclipse.jetty.websocket.common.Generator;
|
||||
import org.eclipse.jetty.websocket.common.WebSocketFrame;
|
||||
import org.eclipse.jetty.websocket.common.frames.BinaryFrame;
|
||||
import org.eclipse.jetty.websocket.common.frames.ContinuationFrame;
|
||||
import org.eclipse.jetty.websocket.common.frames.TextFrame;
|
||||
import org.eclipse.jetty.websocket.servlet.WebSocketServlet;
|
||||
import org.eclipse.jetty.websocket.servlet.WebSocketServletFactory;
|
||||
import org.eclipse.jetty.websocket.tests.Defaults;
|
||||
import org.eclipse.jetty.websocket.tests.SimpleServletServer;
|
||||
import org.eclipse.jetty.websocket.tests.UntrustedWSClient;
|
||||
import org.eclipse.jetty.websocket.tests.UntrustedWSConnection;
|
||||
import org.eclipse.jetty.websocket.tests.UntrustedWSEndpoint;
|
||||
import org.eclipse.jetty.websocket.tests.UntrustedWSSession;
|
||||
import org.junit.After;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.TestName;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
/**
|
||||
* Test various <a href="http://tools.ietf.org/html/rfc6455">RFC 6455</a> specified requirements placed on {@link WebSocketServlet}
|
||||
*/
|
||||
@RunWith(AdvancedRunner.class)
|
||||
public class WebSocketServletRFCTest
|
||||
{
|
||||
private static SimpleServletServer server;
|
||||
|
||||
@BeforeClass
|
||||
public static void startServer() throws Exception
|
||||
{
|
||||
server = new SimpleServletServer(new WebSocketServlet()
|
||||
{
|
||||
@Override
|
||||
public void configure(WebSocketServletFactory factory)
|
||||
{
|
||||
factory.register(RFC6455Socket.class);
|
||||
}
|
||||
});
|
||||
server.start();
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void stopServer() throws Exception
|
||||
{
|
||||
server.stop();
|
||||
}
|
||||
|
||||
@Rule
|
||||
public TestName testname = new TestName();
|
||||
|
||||
private UntrustedWSClient client;
|
||||
|
||||
@Before
|
||||
public void startClient() throws Exception
|
||||
{
|
||||
client = new UntrustedWSClient();
|
||||
client.start();
|
||||
}
|
||||
|
||||
@After
|
||||
public void stopClient() throws Exception
|
||||
{
|
||||
client.stop();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param clazz the class to enable
|
||||
* @param enabled true to enable the stack traces (or not)
|
||||
* @deprecated use {@link StacklessLogging} in a try-with-resources block instead
|
||||
*/
|
||||
@Deprecated
|
||||
private void enableStacks(Class<?> clazz, boolean enabled)
|
||||
{
|
||||
StdErrLog log = StdErrLog.getLogger(clazz);
|
||||
log.setHideStacks(!enabled);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that aggregation of binary frames into a single message occurs
|
||||
*
|
||||
* @throws Exception on test failure
|
||||
*/
|
||||
@Test
|
||||
public void testBinaryAggregate() throws Exception
|
||||
{
|
||||
URI wsUri = server.getServerUri();
|
||||
|
||||
ClientUpgradeRequest req = new ClientUpgradeRequest();
|
||||
Future<UntrustedWSSession> clientConnectFuture = client.connect(wsUri, req);
|
||||
|
||||
UntrustedWSSession clientSession = clientConnectFuture.get(Defaults.CONNECT_TIMEOUT_MS, TimeUnit.MILLISECONDS);
|
||||
UntrustedWSConnection clientConnection = clientSession.getUntrustedConnection();
|
||||
|
||||
// Generate binary frames
|
||||
byte buf1[] = new byte[128];
|
||||
byte buf2[] = new byte[128];
|
||||
byte buf3[] = new byte[128];
|
||||
|
||||
Arrays.fill(buf1, (byte) 0xAA);
|
||||
Arrays.fill(buf2, (byte) 0xBB);
|
||||
Arrays.fill(buf3, (byte) 0xCC);
|
||||
|
||||
WebSocketFrame bin;
|
||||
|
||||
bin = new BinaryFrame().setPayload(buf1).setFin(false);
|
||||
|
||||
clientConnection.write(bin); // write buf1 (fin=false)
|
||||
|
||||
bin = new ContinuationFrame().setPayload(buf2).setFin(false);
|
||||
|
||||
clientConnection.write(bin); // write buf2 (fin=false)
|
||||
|
||||
bin = new ContinuationFrame().setPayload(buf3).setFin(true);
|
||||
|
||||
clientConnection.write(bin); // write buf3 (fin=true)
|
||||
|
||||
// Read frame echo'd back (hopefully a single binary frame)
|
||||
WebSocketFrame incomingFrame = clientSession.getUntrustedEndpoint().framesQueue.poll(5, TimeUnit.SECONDS);
|
||||
|
||||
int expectedSize = buf1.length + buf2.length + buf3.length;
|
||||
assertThat("BinaryFrame.payloadLength", incomingFrame.getPayloadLength(), is(expectedSize));
|
||||
|
||||
int aaCount = 0;
|
||||
int bbCount = 0;
|
||||
int ccCount = 0;
|
||||
|
||||
ByteBuffer echod = incomingFrame.getPayload();
|
||||
while (echod.remaining() >= 1)
|
||||
{
|
||||
byte b = echod.get();
|
||||
switch (b)
|
||||
{
|
||||
case (byte) 0xAA:
|
||||
aaCount++;
|
||||
break;
|
||||
case (byte) 0xBB:
|
||||
bbCount++;
|
||||
break;
|
||||
case (byte) 0xCC:
|
||||
ccCount++;
|
||||
break;
|
||||
default:
|
||||
Assert.fail(String.format("Encountered invalid byte 0x%02X", (byte) (0xFF & b)));
|
||||
}
|
||||
}
|
||||
|
||||
assertThat("Echoed data count for 0xAA", aaCount, is(buf1.length));
|
||||
assertThat("Echoed data count for 0xBB", bbCount, is(buf2.length));
|
||||
assertThat("Echoed data count for 0xCC", ccCount, is(buf3.length));
|
||||
|
||||
clientSession.close();
|
||||
}
|
||||
|
||||
@Test(expected = NotUtf8Exception.class)
|
||||
public void testDetectBadUTF8()
|
||||
{
|
||||
byte buf[] = new byte[]
|
||||
{(byte) 0xC2, (byte) 0xC3};
|
||||
|
||||
Utf8StringBuilder utf = new Utf8StringBuilder();
|
||||
utf.append(buf, 0, buf.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the requirement of responding with server terminated close code 1011 when there is an unhandled (internal server error) being produced by the
|
||||
* WebSocket POJO.
|
||||
*
|
||||
* @throws Exception on test failure
|
||||
*/
|
||||
@Test
|
||||
public void testInternalError() throws Exception
|
||||
{
|
||||
URI wsUri = server.getServerUri();
|
||||
|
||||
ClientUpgradeRequest req = new ClientUpgradeRequest();
|
||||
Future<UntrustedWSSession> clientConnectFuture = client.connect(wsUri, req);
|
||||
|
||||
UntrustedWSSession clientSession = clientConnectFuture.get(Defaults.CONNECT_TIMEOUT_MS, TimeUnit.MILLISECONDS);
|
||||
UntrustedWSEndpoint clientSocket = clientSession.getUntrustedEndpoint();
|
||||
|
||||
clientSession.getRemote().sendString("CRASH");
|
||||
clientSocket.awaitCloseEvent("Client");
|
||||
clientSocket.assertCloseInfo("Client", StatusCode.SERVER_ERROR, anything());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test http://tools.ietf.org/html/rfc6455#section-4.1 where server side upgrade handling is supposed to be case insensitive.
|
||||
* <p>
|
||||
* This test will simulate a client requesting upgrade with all lowercase headers.
|
||||
*
|
||||
* @throws Exception on test failure
|
||||
*/
|
||||
@Test
|
||||
public void testLowercaseUpgrade() throws Exception
|
||||
{
|
||||
URI wsUri = server.getServerUri();
|
||||
|
||||
ClientUpgradeRequest req = new ClientUpgradeRequest();
|
||||
req.setHeader("upgrade", "websocket");
|
||||
req.setHeader("connection", "upgrade");
|
||||
req.setHeader("sec-websocket-key", UntrustedWSClient.getStaticWebSocketKey());
|
||||
req.setHeader("sec-websocket-origin", wsUri.toASCIIString());
|
||||
req.setHeader("sec-websocket-protocol", "echo");
|
||||
req.setHeader("sec-websocket-version", "13");
|
||||
Future<UntrustedWSSession> clientConnectFuture = client.connect(wsUri, req);
|
||||
|
||||
UntrustedWSSession clientSession = clientConnectFuture.get(Defaults.CONNECT_TIMEOUT_MS, TimeUnit.MILLISECONDS);
|
||||
UntrustedWSEndpoint clientSocket = clientSession.getUntrustedEndpoint();
|
||||
|
||||
// Generate text frame
|
||||
String msg = "this is an echo ... cho ... ho ... o";
|
||||
clientSocket.getRemote().sendString(msg);
|
||||
|
||||
// Read frame (hopefully text frame)
|
||||
String incomingMessage = clientSocket.messageQueue.poll(5, TimeUnit.SECONDS);
|
||||
assertThat("Incoming Message", incomingMessage, is(msg));
|
||||
|
||||
clientSession.close();
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Test http://tools.ietf.org/html/rfc6455#section-4.1 where server side upgrade handling is supposed to be case insensitive.
|
||||
* <p>
|
||||
* This test will simulate a client requesting upgrade with all uppercase headers.
|
||||
*
|
||||
* @throws Exception on test failure
|
||||
*/
|
||||
@Test
|
||||
public void testUppercaseUpgrade() throws Exception
|
||||
{
|
||||
URI wsUri = server.getServerUri();
|
||||
|
||||
ClientUpgradeRequest req = new ClientUpgradeRequest();
|
||||
req.setHeader("UPGRADE", "WEBSOCKET");
|
||||
req.setHeader("CONNECTION", "UPGRADE");
|
||||
req.setHeader("SEC-WEBSOCKET-KEY", UntrustedWSClient.getStaticWebSocketKey());
|
||||
req.setHeader("SEC-WEBSOCKET-ORIGIN", wsUri.toASCIIString());
|
||||
req.setHeader("SEC-WEBSOCKET-PROTOCOL", "ECHO");
|
||||
req.setHeader("SEC-WEBSOCKET-VERSION", "13");
|
||||
Future<UntrustedWSSession> clientConnectFuture = client.connect(wsUri, req);
|
||||
|
||||
UntrustedWSSession clientSession = clientConnectFuture.get(Defaults.CONNECT_TIMEOUT_MS, TimeUnit.MILLISECONDS);
|
||||
UntrustedWSEndpoint clientSocket = clientSession.getUntrustedEndpoint();
|
||||
|
||||
// Generate text frame
|
||||
String msg = "this is an echo ... cho ... ho ... o";
|
||||
clientSocket.getRemote().sendString(msg);
|
||||
|
||||
// Read frame (hopefully text frame)
|
||||
String incomingMessage = clientSocket.messageQueue.poll(5, TimeUnit.SECONDS);
|
||||
assertThat("Incoming Message", incomingMessage, is(msg));
|
||||
|
||||
clientSession.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTextNotUTF8() throws Exception
|
||||
{
|
||||
URI wsUri = server.getServerUri();
|
||||
|
||||
ClientUpgradeRequest req = new ClientUpgradeRequest();
|
||||
req.setSubProtocols("other");
|
||||
Future<UntrustedWSSession> clientConnectFuture = client.connect(wsUri, req);
|
||||
|
||||
UntrustedWSSession clientSession = clientConnectFuture.get(Defaults.CONNECT_TIMEOUT_MS, TimeUnit.MILLISECONDS);
|
||||
UntrustedWSConnection clientConnection = clientSession.getUntrustedConnection();
|
||||
UntrustedWSEndpoint clientSocket = clientSession.getUntrustedEndpoint();
|
||||
|
||||
byte buf[] = new byte[]{(byte) 0xC2, (byte) 0xC3};
|
||||
|
||||
Generator generator = new Generator(WebSocketPolicy.newServerPolicy(), client.getBufferPool(), false);
|
||||
|
||||
WebSocketFrame txt = new TextFrame().setPayload(ByteBuffer.wrap(buf));
|
||||
txt.setMask(Hex.asByteArray("11223344"));
|
||||
ByteBuffer bbHeader = generator.generateHeaderBytes(txt);
|
||||
|
||||
clientConnection.writeRaw(bbHeader);
|
||||
clientConnection.writeRaw(txt.getPayload());
|
||||
|
||||
clientSocket.awaitCloseEvent("Client");
|
||||
clientSocket.assertCloseInfo("Client", StatusCode.BAD_PAYLOAD, anything());
|
||||
}
|
||||
}
|
|
@ -16,7 +16,7 @@
|
|||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.websocket.server;
|
||||
package org.eclipse.jetty.websocket.tests.server;
|
||||
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.notNullValue;
|
||||
|
@ -27,6 +27,7 @@ import java.net.URI;
|
|||
import java.util.ArrayList;
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
|
@ -36,14 +37,23 @@ import org.eclipse.jetty.server.Server;
|
|||
import org.eclipse.jetty.server.ServerConnector;
|
||||
import org.eclipse.jetty.servlet.FilterHolder;
|
||||
import org.eclipse.jetty.servlet.ServletContextHandler;
|
||||
import org.eclipse.jetty.toolchain.test.EventQueue;
|
||||
import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
|
||||
import org.eclipse.jetty.webapp.WebAppContext;
|
||||
import org.eclipse.jetty.websocket.common.WebSocketFrame;
|
||||
import org.eclipse.jetty.websocket.common.frames.TextFrame;
|
||||
import org.eclipse.jetty.websocket.common.test.XBlockheadClient;
|
||||
import org.eclipse.jetty.websocket.api.Session;
|
||||
import org.eclipse.jetty.websocket.api.util.WSURI;
|
||||
import org.eclipse.jetty.websocket.client.ClientUpgradeRequest;
|
||||
import org.eclipse.jetty.websocket.client.WebSocketClient;
|
||||
import org.eclipse.jetty.websocket.server.NativeWebSocketConfiguration;
|
||||
import org.eclipse.jetty.websocket.server.WebSocketUpgradeFilter;
|
||||
import org.eclipse.jetty.websocket.servlet.WebSocketCreator;
|
||||
import org.eclipse.jetty.websocket.tests.Defaults;
|
||||
import org.eclipse.jetty.websocket.tests.TrackingEndpoint;
|
||||
import org.eclipse.jetty.websocket.tests.WSServer;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.TestName;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.Parameterized;
|
||||
|
||||
|
@ -279,86 +289,90 @@ public class WebSocketUpgradeFilterTest
|
|||
return cases;
|
||||
}
|
||||
|
||||
@Rule
|
||||
public TestName testname = new TestName();
|
||||
|
||||
private WebSocketClient client;
|
||||
|
||||
@Before
|
||||
public void startClient() throws Exception
|
||||
{
|
||||
client = new WebSocketClient();
|
||||
client.start();
|
||||
}
|
||||
|
||||
@After
|
||||
public void stopClient() throws Exception
|
||||
{
|
||||
client.stop();
|
||||
}
|
||||
|
||||
private final Server server;
|
||||
private final URI serverUri;
|
||||
|
||||
public WebSocketUpgradeFilterTest(String testId, ServerProvider serverProvider) throws Exception
|
||||
{
|
||||
this.server = serverProvider.newServer();
|
||||
|
||||
ServerConnector connector = (ServerConnector) server.getConnectors()[0];
|
||||
|
||||
// Establish the Server URI
|
||||
String host = connector.getHost();
|
||||
if (host == null)
|
||||
{
|
||||
host = "localhost";
|
||||
}
|
||||
int port = connector.getLocalPort();
|
||||
|
||||
serverUri = new URI(String.format("ws://%s:%d/", host, port));
|
||||
serverUri = WSURI.toWebsocket(server.getURI());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNormalConfiguration() throws Exception
|
||||
{
|
||||
URI destUri = serverUri.resolve("/info/");
|
||||
URI wsUri = serverUri.resolve("/info/");
|
||||
|
||||
TrackingEndpoint clientSocket = new TrackingEndpoint(testname.getMethodName());
|
||||
ClientUpgradeRequest upgradeRequest = new ClientUpgradeRequest();
|
||||
Future<Session> clientConnectFuture = client.connect(clientSocket, wsUri, upgradeRequest);
|
||||
|
||||
Session clientSession = clientConnectFuture.get(Defaults.CONNECT_TIMEOUT_MS, TimeUnit.MILLISECONDS);
|
||||
clientSession.getRemote().sendString("hello");
|
||||
|
||||
try (XBlockheadClient client = new XBlockheadClient(destUri))
|
||||
{
|
||||
client.connect();
|
||||
client.sendStandardRequest();
|
||||
client.expectUpgradeResponse();
|
||||
|
||||
client.write(new TextFrame().setPayload("hello"));
|
||||
|
||||
EventQueue<WebSocketFrame> frames = client.readFrames(1, 1000, TimeUnit.MILLISECONDS);
|
||||
String payload = frames.poll().getPayloadAsUTF8();
|
||||
|
||||
// If we can connect and send a text message, we know that the endpoint was
|
||||
// added properly, and the response will help us verify the policy configuration too
|
||||
assertThat("payload", payload, containsString("session.maxTextMessageSize=" + (10 * 1024 * 1024)));
|
||||
}
|
||||
String incomingMessage = clientSocket.messageQueue.poll(5, TimeUnit.SECONDS);
|
||||
|
||||
// If we can connect and send a text message, we know that the endpoint was
|
||||
// added properly, and the response will help us verify the policy configuration too
|
||||
assertThat("Client incoming message", incomingMessage, containsString("session.maxTextMessageSize=" + (10 * 1024 * 1024)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStopStartOfHandler() throws Exception
|
||||
{
|
||||
URI destUri = serverUri.resolve("/info/");
|
||||
|
||||
try (XBlockheadClient client = new XBlockheadClient(destUri))
|
||||
{
|
||||
client.connect();
|
||||
client.sendStandardRequest();
|
||||
client.expectUpgradeResponse();
|
||||
|
||||
client.write(new TextFrame().setPayload("hello 1"));
|
||||
|
||||
EventQueue<WebSocketFrame> frames = client.readFrames(1, 1000, TimeUnit.MILLISECONDS);
|
||||
String payload = frames.poll().getPayloadAsUTF8();
|
||||
|
||||
// If we can connect and send a text message, we know that the endpoint was
|
||||
// added properly, and the response will help us verify the policy configuration too
|
||||
assertThat("payload", payload, containsString("session.maxTextMessageSize=" + (10 * 1024 * 1024)));
|
||||
}
|
||||
URI wsUri = serverUri.resolve("/info/");
|
||||
|
||||
TrackingEndpoint clientSocket = new TrackingEndpoint(testname.getMethodName());
|
||||
ClientUpgradeRequest upgradeRequest = new ClientUpgradeRequest();
|
||||
Future<Session> clientConnectFuture = client.connect(clientSocket, wsUri, upgradeRequest);
|
||||
|
||||
Session clientSession = clientConnectFuture.get(Defaults.CONNECT_TIMEOUT_MS, TimeUnit.MILLISECONDS);
|
||||
clientSession.getRemote().sendString("hello 1");
|
||||
|
||||
String incomingMessage = clientSocket.messageQueue.poll(5, TimeUnit.SECONDS);
|
||||
|
||||
// If we can connect and send a text message, we know that the endpoint was
|
||||
// added properly, and the response will help us verify the policy configuration too
|
||||
assertThat("Client incoming message", incomingMessage, containsString("session.maxTextMessageSize=" + (10 * 1024 * 1024)));
|
||||
|
||||
clientSession.close();
|
||||
|
||||
server.getHandler().stop();
|
||||
server.getHandler().start();
|
||||
|
||||
try (XBlockheadClient client = new XBlockheadClient(destUri))
|
||||
{
|
||||
client.connect();
|
||||
client.sendStandardRequest();
|
||||
client.expectUpgradeResponse();
|
||||
|
||||
client.write(new TextFrame().setPayload("hello 2"));
|
||||
|
||||
EventQueue<WebSocketFrame> frames = client.readFrames(1, 1000, TimeUnit.MILLISECONDS);
|
||||
String payload = frames.poll().getPayloadAsUTF8();
|
||||
|
||||
// If we can connect and send a text message, we know that the endpoint was
|
||||
// added properly, and the response will help us verify the policy configuration too
|
||||
assertThat("payload", payload, containsString("session.maxTextMessageSize=" + (10 * 1024 * 1024)));
|
||||
}
|
||||
// Make request again (server should have retained websocket configuration)
|
||||
|
||||
clientSocket = new TrackingEndpoint(testname.getMethodName());
|
||||
upgradeRequest = new ClientUpgradeRequest();
|
||||
clientConnectFuture = client.connect(clientSocket, wsUri, upgradeRequest);
|
||||
|
||||
clientSession = clientConnectFuture.get(Defaults.CONNECT_TIMEOUT_MS, TimeUnit.MILLISECONDS);
|
||||
clientSession.getRemote().sendString("hello 2");
|
||||
|
||||
incomingMessage = clientSocket.messageQueue.poll(5, TimeUnit.SECONDS);
|
||||
|
||||
// If we can connect and send a text message, we know that the endpoint was
|
||||
// added properly, and the response will help us verify the policy configuration too
|
||||
assertThat("Client incoming message", incomingMessage, containsString("session.maxTextMessageSize=" + (10 * 1024 * 1024)));
|
||||
|
||||
clientSession.close();
|
||||
}
|
||||
}
|
|
@ -389,8 +389,6 @@ public class ConfiguratorTest
|
|||
|
||||
server.start();
|
||||
baseServerUri = WSURI.toWebsocket(server.getURI()).resolve("/");
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Server started on {}", baseServerUri);
|
||||
}
|
||||
|
||||
public static String toSafeAddr(InetSocketAddress addr)
|
||||
|
|
Loading…
Reference in New Issue