Issue #207 - Final removal of BlockheadClient

This commit is contained in:
Joakim Erdfelt 2017-04-27 11:21:04 -07:00
parent 7f345fc2d4
commit ae51344582
49 changed files with 3254 additions and 3373 deletions

View File

@ -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

View File

@ -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));
}
}

View File

@ -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;
}

View File

@ -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();
}
}
}

View File

@ -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;
}
}

View File

@ -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();
}
}
}

View File

@ -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();
}
}
}

View File

@ -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));
}
}
}

View File

@ -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();
}
}
}

View File

@ -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();
}
}
}

View File

@ -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();
}
}
}

View File

@ -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();
}
}
}

View File

@ -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();
}
}
}

View File

@ -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();
}
}
}

View File

@ -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>"));
}
}
}

View File

@ -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();
}
}
}

View File

@ -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));
}
}
}

View File

@ -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

View File

@ -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());

View File

@ -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));
}
}

View File

@ -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);
}
}
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}
}

View File

@ -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());
}
}

View File

@ -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();
}
}

View File

@ -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();
}
}

View File

@ -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();
}
}

View File

@ -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();
}
}

View File

@ -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();
}
}

View File

@ -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();
}
}

View File

@ -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();
}
}

View File

@ -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();
}
}

View File

@ -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();
}
}

View File

@ -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();
}
}

View File

@ -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);
}
}

View File

@ -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());
}
}

View File

@ -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();
}
}

View File

@ -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();
}
}
}

View File

@ -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);
}
}

View File

@ -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();
}
}

View File

@ -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();
}
}

View File

@ -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();
}
}

View File

@ -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();
}
}

View File

@ -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();
}
}

View File

@ -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");
}
}

View File

@ -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>"));
}
}

View File

@ -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());
}
}

View File

@ -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();
}
}

View File

@ -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)