From 8e889287fa4484ddc4cdb9bd8a1ebb5f8c478435 Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Thu, 27 Apr 2017 11:21:04 -0700 Subject: [PATCH] Issue #207 - Final removal of BlockheadClient # Conflicts: # jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/test/XBlockheadClient.java # jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/RequestHeadersTest.java --- .../test/resources/jetty-logging.properties | 1 - .../test/BlockheadClientConstructionTest.java | 72 -- .../common/test/IBlockheadClient.java | 79 -- .../common/test/XBlockheadClient.java | 827 ------------------ .../jetty/websocket/common/test/XFuzzer.java | 325 ------- .../server/AnnotatedMaxMessageSizeTest.java | 147 ---- .../jetty/websocket/server/ChromeTest.java | 85 -- .../jetty/websocket/server/FirefoxTest.java | 74 -- .../server/FragmentExtensionTest.java | 104 --- .../server/IdentityExtensionTest.java | 84 -- .../websocket/server/IdleTimeoutTest.java | 115 --- .../websocket/server/RequestHeadersTest.java | 162 ---- .../websocket/server/TooFastClientTest.java | 183 ---- .../server/WebSocketInvalidVersionTest.java | 73 -- .../server/WebSocketServerSessionTest.java | 104 --- .../server/WebSocketServletRFCTest.java | 343 -------- .../misbehaving/MisbehavingClassTest.java | 130 --- .../test/resources/jetty-logging.properties | 2 +- .../tests/AbstractTrackingEndpoint.java | 2 +- .../websocket/tests/UntrustedWSClient.java | 14 + .../jetty/websocket/tests/WSServer.java | 231 +++++ .../websocket/tests/servlets/EchoServlet.java | 35 + .../websocket/tests/servlets/EchoSocket.java | 54 ++ .../server/AnnotatedMaxMessageSizeTest.java | 189 ++++ .../websocket/tests/server/ChromeTest.java | 112 +++ .../tests}/server/DecoratorsLegacyTest.java | 107 ++- .../tests}/server/DecoratorsTest.java | 82 +- .../websocket/tests/server/FirefoxTest.java | 109 +++ .../tests/server/FragmentExtensionTest.java | 133 +++ .../tests/server/IdentityExtensionTest.java | 113 +++ .../tests/server/IdleTimeoutTest.java | 131 +++ .../InfoContextAltAttributeListener.java | 52 ++ .../server/InfoContextAttributeListener.java | 49 ++ .../tests/server/InfoContextListener.java | 50 ++ .../websocket/tests/server/InfoServlet.java | 47 + .../websocket/tests/server/InfoSocket.java | 44 + .../server/ManyConnectionsCleanupTest.java | 299 ++++--- .../tests/server/MisbehavingClassTest.java | 257 ++++++ .../websocket/tests/server/RFC6455Socket.java | 83 ++ .../tests/server/RequestHeadersTest.java | 105 +++ .../tests}/server/SubProtocolTest.java | 78 +- .../tests}/server/SuspendResumeTest.java | 83 +- .../tests/server/TooFastClientTest.java | 221 +++++ .../tests}/server/WebSocketCloseTest.java | 262 +++--- .../server/WebSocketInvalidVersionTest.java | 90 ++ .../server/WebSocketServerSessionTest.java | 238 +++++ .../tests/server/WebSocketServletRFCTest.java | 328 +++++++ .../server/WebSocketUpgradeFilterTest.java | 144 +-- .../tests/server/jsr356/ConfiguratorTest.java | 2 - 49 files changed, 3254 insertions(+), 3400 deletions(-) delete mode 100644 jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/test/BlockheadClientConstructionTest.java delete mode 100644 jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/test/IBlockheadClient.java delete mode 100644 jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/test/XBlockheadClient.java delete mode 100644 jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/test/XFuzzer.java delete mode 100644 jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/AnnotatedMaxMessageSizeTest.java delete mode 100644 jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ChromeTest.java delete mode 100644 jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/FirefoxTest.java delete mode 100644 jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/FragmentExtensionTest.java delete mode 100644 jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/IdentityExtensionTest.java delete mode 100644 jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/IdleTimeoutTest.java delete mode 100644 jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/RequestHeadersTest.java delete mode 100644 jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/TooFastClientTest.java delete mode 100644 jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/WebSocketInvalidVersionTest.java delete mode 100644 jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/WebSocketServerSessionTest.java delete mode 100644 jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/WebSocketServletRFCTest.java delete mode 100644 jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/misbehaving/MisbehavingClassTest.java create mode 100644 jetty-websocket/websocket-tests/src/main/java/org/eclipse/jetty/websocket/tests/WSServer.java create mode 100644 jetty-websocket/websocket-tests/src/main/java/org/eclipse/jetty/websocket/tests/servlets/EchoServlet.java create mode 100644 jetty-websocket/websocket-tests/src/main/java/org/eclipse/jetty/websocket/tests/servlets/EchoSocket.java create mode 100644 jetty-websocket/websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/server/AnnotatedMaxMessageSizeTest.java create mode 100644 jetty-websocket/websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/server/ChromeTest.java rename jetty-websocket/{websocket-server/src/test/java/org/eclipse/jetty/websocket => websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests}/server/DecoratorsLegacyTest.java (69%) rename jetty-websocket/{websocket-server/src/test/java/org/eclipse/jetty/websocket => websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests}/server/DecoratorsTest.java (72%) create mode 100644 jetty-websocket/websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/server/FirefoxTest.java create mode 100644 jetty-websocket/websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/server/FragmentExtensionTest.java create mode 100644 jetty-websocket/websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/server/IdentityExtensionTest.java create mode 100644 jetty-websocket/websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/server/IdleTimeoutTest.java create mode 100644 jetty-websocket/websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/server/InfoContextAltAttributeListener.java create mode 100644 jetty-websocket/websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/server/InfoContextAttributeListener.java create mode 100644 jetty-websocket/websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/server/InfoContextListener.java create mode 100644 jetty-websocket/websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/server/InfoServlet.java create mode 100644 jetty-websocket/websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/server/InfoSocket.java rename jetty-websocket/{websocket-server/src/test/java/org/eclipse/jetty/websocket => websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests}/server/ManyConnectionsCleanupTest.java (56%) create mode 100644 jetty-websocket/websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/server/MisbehavingClassTest.java create mode 100644 jetty-websocket/websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/server/RFC6455Socket.java create mode 100644 jetty-websocket/websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/server/RequestHeadersTest.java rename jetty-websocket/{websocket-server/src/test/java/org/eclipse/jetty/websocket => websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests}/server/SubProtocolTest.java (62%) rename jetty-websocket/{websocket-server/src/test/java/org/eclipse/jetty/websocket => websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests}/server/SuspendResumeTest.java (61%) create mode 100644 jetty-websocket/websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/server/TooFastClientTest.java rename jetty-websocket/{websocket-server/src/test/java/org/eclipse/jetty/websocket => websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests}/server/WebSocketCloseTest.java (51%) create mode 100644 jetty-websocket/websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/server/WebSocketInvalidVersionTest.java create mode 100644 jetty-websocket/websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/server/WebSocketServerSessionTest.java create mode 100644 jetty-websocket/websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/server/WebSocketServletRFCTest.java rename jetty-websocket/{websocket-server/src/test/java/org/eclipse/jetty/websocket => websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests}/server/WebSocketUpgradeFilterTest.java (75%) diff --git a/jetty-websocket/websocket-client/src/test/resources/jetty-logging.properties b/jetty-websocket/websocket-client/src/test/resources/jetty-logging.properties index 48904ea973d..b3767736b29 100644 --- a/jetty-websocket/websocket-client/src/test/resources/jetty-logging.properties +++ b/jetty-websocket/websocket-client/src/test/resources/jetty-logging.properties @@ -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 diff --git a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/test/BlockheadClientConstructionTest.java b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/test/BlockheadClientConstructionTest.java deleted file mode 100644 index 05380bdabe6..00000000000 --- a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/test/BlockheadClientConstructionTest.java +++ /dev/null @@ -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 data() - { - List 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)); - } - -} diff --git a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/test/IBlockheadClient.java b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/test/IBlockheadClient.java deleted file mode 100644 index cf952fece02..00000000000 --- a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/test/IBlockheadClient.java +++ /dev/null @@ -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 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; -} \ No newline at end of file diff --git a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/test/XBlockheadClient.java b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/test/XBlockheadClient.java deleted file mode 100644 index 5d942123268..00000000000 --- a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/test/XBlockheadClient.java +++ /dev/null @@ -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. - *

- * This client will use {@link HttpURLConnection} and {@link HttpsURLConnection} with standard blocking calls to perform websocket requests. - *

- * This client is NOT 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. - *

- * 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 what 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 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 extensions = new ArrayList<>(); - private List 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 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 getExtensionConfigs(HttpResponse response) - { - List 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 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.getRawQuery()); - } - 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 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(); - } - } -} diff --git a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/test/XFuzzer.java b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/test/XFuzzer.java deleted file mode 100644 index 70e2f2f67a3..00000000000 --- a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/test/XFuzzer.java +++ /dev/null @@ -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 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 expect) throws Exception - { - expect(expect,10,TimeUnit.SECONDS); - } - - public void expect(List expect, int duration, TimeUnit unit) throws Exception - { - int expectedCount = expect.size(); - LOG.debug("expect() {} frame(s)",expect.size()); - - // Read frames - EventQueue 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 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 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; - } -} diff --git a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/AnnotatedMaxMessageSizeTest.java b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/AnnotatedMaxMessageSizeTest.java deleted file mode 100644 index e75e60b3354..00000000000 --- a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/AnnotatedMaxMessageSizeTest.java +++ /dev/null @@ -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 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 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(); - } - } -} diff --git a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ChromeTest.java b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ChromeTest.java deleted file mode 100644 index cd597e427f3..00000000000 --- a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ChromeTest.java +++ /dev/null @@ -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 frames = client.readFrames(1,30,TimeUnit.SECONDS); - WebSocketFrame tf = frames.poll(); - Assert.assertThat("Text Frame.status code",tf.getPayloadAsUTF8(),is(msg)); - } - finally - { - client.close(); - } - } -} diff --git a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/FirefoxTest.java b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/FirefoxTest.java deleted file mode 100644 index d498b94807f..00000000000 --- a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/FirefoxTest.java +++ /dev/null @@ -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 frames = client.readFrames(1, 30, TimeUnit.SECONDS); - WebSocketFrame tf = frames.poll(); - Assert.assertThat("Text Frame.status code", tf.getPayloadAsUTF8(), is(msg)); - } - } -} diff --git a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/FragmentExtensionTest.java b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/FragmentExtensionTest.java deleted file mode 100644 index 9ed72a8b047..00000000000 --- a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/FragmentExtensionTest.java +++ /dev/null @@ -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 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(); - } - } -} diff --git a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/IdentityExtensionTest.java b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/IdentityExtensionTest.java deleted file mode 100644 index 07eedbf9eb7..00000000000 --- a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/IdentityExtensionTest.java +++ /dev/null @@ -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 frames = client.readFrames(1,1000,TimeUnit.MILLISECONDS); - WebSocketFrame frame = frames.poll(); - Assert.assertThat("TEXT.payload",frame.getPayloadAsUTF8(),is("Hello")); - } - finally - { - client.close(); - } - } -} diff --git a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/IdleTimeoutTest.java b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/IdleTimeoutTest.java deleted file mode 100644 index 68d2c0884cf..00000000000 --- a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/IdleTimeoutTest.java +++ /dev/null @@ -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 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(); - } - } -} diff --git a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/RequestHeadersTest.java b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/RequestHeadersTest.java deleted file mode 100644 index 476a4db5d73..00000000000 --- a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/RequestHeadersTest.java +++ /dev/null @@ -1,162 +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 static org.junit.Assert.assertThat; - -import java.net.HttpCookie; -import java.net.URI; -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.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(); - assertThat("Last Request",req,notNullValue()); - List cookies = req.getCookies(); - assertThat("Request cookies",cookies,notNullValue()); - assertThat("Request cookies.size",cookies.size(),is(2)); - for (HttpCookie cookie : cookies) - { - assertThat("Cookie name",cookie.getName(),anyOf(is("fruit"),is("type"))); - assertThat("Cookie value",cookie.getValue(),anyOf(is("Pear"),is("Anjou"))); - } - } - finally - { - client.close(); - } - } - - @Test - public void testRequestURI() throws Exception - { - URI destUri = server.getServerUri().resolve("/?abc=x%20z&breakfast=bacon%26eggs&2*2%3d5=false"); - BlockheadClient client = new BlockheadClient(destUri); - client.setTimeout(1,TimeUnit.SECONDS); - - try - { - client.connect(); - client.sendStandardRequest(); - client.expectUpgradeResponse(); - - UpgradeRequest req = echoCreator.getLastRequest(); - assertThat("Last Request",req,notNullValue()); - assertThat("Request.host", req.getHost(), is(server.getServerUri().getHost())); - assertThat("Request.queryString", req.getQueryString(), is("abc=x%20z&breakfast=bacon%26eggs&2*2%3d5=false")); - assertThat("Request.uri.path", req.getRequestURI().getPath(), is("/")); - assertThat("Request.uri.rawQuery", req.getRequestURI().getRawQuery(), is("abc=x%20z&breakfast=bacon%26eggs&2*2%3d5=false")); - assertThat("Request.uri.query", req.getRequestURI().getQuery(), is("abc=x z&breakfast=bacon&eggs&2*2=5=false")); - } - finally - { - client.close(); - } - } -} diff --git a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/TooFastClientTest.java b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/TooFastClientTest.java deleted file mode 100644 index f266acc586f..00000000000 --- a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/TooFastClientTest.java +++ /dev/null @@ -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. - *

- * 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 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 frames = client.readFrames(1,1,TimeUnit.SECONDS); - - WebSocketFrame tf = frames.poll(); - Assert.assertThat("Text Frame/msg1",tf.getPayloadAsUTF8(),is(bigMsg)); - } - finally - { - client.close(); - } - } - - -} diff --git a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/WebSocketInvalidVersionTest.java b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/WebSocketInvalidVersionTest.java deleted file mode 100644 index a0e0091e69b..00000000000 --- a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/WebSocketInvalidVersionTest.java +++ /dev/null @@ -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(); - } - } -} diff --git a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/WebSocketServerSessionTest.java b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/WebSocketServerSessionTest.java deleted file mode 100644 index b34894bf15d..00000000000 --- a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/WebSocketServerSessionTest.java +++ /dev/null @@ -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 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("")); - } - } -} diff --git a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/WebSocketServletRFCTest.java b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/WebSocketServletRFCTest.java deleted file mode 100644 index beb4bc664f4..00000000000 --- a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/WebSocketServletRFCTest.java +++ /dev/null @@ -1,343 +0,0 @@ -// -// ======================================================================== -// Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd. -// ------------------------------------------------------------------------ -// All rights reserved. This program and the accompanying materials -// are made available under the terms of the Eclipse Public License v1.0 -// and Apache License v2.0 which accompanies this distribution. -// -// The Eclipse Public License is available at -// http://www.eclipse.org/legal/epl-v10.html -// -// The Apache License v2.0 is available at -// http://www.opensource.org/licenses/apache2.0.php -// -// You may elect to redistribute this code under either of these licenses. -// ======================================================================== -// - -package org.eclipse.jetty.websocket.server; - -import static org.hamcrest.Matchers.is; - -import java.nio.ByteBuffer; -import java.util.Arrays; -import java.util.concurrent.TimeUnit; - -import org.eclipse.jetty.toolchain.test.AdvancedRunner; -import org.eclipse.jetty.toolchain.test.EventQueue; -import org.eclipse.jetty.util.Utf8Appendable.NotUtf8Exception; -import org.eclipse.jetty.util.Utf8StringBuilder; -import org.eclipse.jetty.util.log.StacklessLogging; -import org.eclipse.jetty.util.log.StdErrLog; -import org.eclipse.jetty.websocket.api.StatusCode; -import org.eclipse.jetty.websocket.api.extensions.Frame; -import org.eclipse.jetty.websocket.common.CloseInfo; -import org.eclipse.jetty.websocket.common.Generator; -import org.eclipse.jetty.websocket.common.OpCode; -import org.eclipse.jetty.websocket.common.Parser; -import org.eclipse.jetty.websocket.common.WebSocketFrame; -import org.eclipse.jetty.websocket.common.frames.BinaryFrame; -import org.eclipse.jetty.websocket.common.frames.ContinuationFrame; -import org.eclipse.jetty.websocket.common.frames.TextFrame; -import org.eclipse.jetty.websocket.common.test.XBlockheadClient; -import org.eclipse.jetty.websocket.common.test.UnitGenerator; -import org.eclipse.jetty.websocket.common.util.Hex; -import org.eclipse.jetty.websocket.server.helper.RFCServlet; -import org.eclipse.jetty.websocket.server.helper.RFCSocket; -import org.eclipse.jetty.websocket.servlet.WebSocketServlet; -import org.junit.AfterClass; -import org.junit.Assert; -import org.junit.BeforeClass; -import org.junit.Test; -import org.junit.runner.RunWith; - -/** - * Test various RFC 6455 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 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 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 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. - *

- * 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 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 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. - *

- * 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 frames = client.readFrames(1,30,TimeUnit.SECONDS); - WebSocketFrame tf = frames.poll(); - Assert.assertThat("Text Frame.status code",tf.getPayloadAsUTF8(),is(msg)); - } - finally - { - client.close(); - } - } -} diff --git a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/misbehaving/MisbehavingClassTest.java b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/misbehaving/MisbehavingClassTest.java deleted file mode 100644 index d822e279975..00000000000 --- a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/misbehaving/MisbehavingClassTest.java +++ /dev/null @@ -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 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 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)); - } - } -} diff --git a/jetty-websocket/websocket-server/src/test/resources/jetty-logging.properties b/jetty-websocket/websocket-server/src/test/resources/jetty-logging.properties index c454c69fff6..1b81db677b9 100644 --- a/jetty-websocket/websocket-server/src/test/resources/jetty-logging.properties +++ b/jetty-websocket/websocket-server/src/test/resources/jetty-logging.properties @@ -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 \ No newline at end of file +org.eclipse.jetty.websocket.tests.server.WebSocketCloseTest$FastFailSocket.STACKS=OFF \ No newline at end of file diff --git a/jetty-websocket/websocket-tests/src/main/java/org/eclipse/jetty/websocket/tests/AbstractTrackingEndpoint.java b/jetty-websocket/websocket-tests/src/main/java/org/eclipse/jetty/websocket/tests/AbstractTrackingEndpoint.java index 48fce847183..124a9ea0a95 100644 --- a/jetty-websocket/websocket-tests/src/main/java/org/eclipse/jetty/websocket/tests/AbstractTrackingEndpoint.java +++ b/jetty-websocket/websocket-tests/src/main/java/org/eclipse/jetty/websocket/tests/AbstractTrackingEndpoint.java @@ -49,7 +49,7 @@ public abstract class AbstractTrackingEndpoint LOG = Log.getLogger(this.getClass().getName() + "." + id); } - public void assertCloseInfo(String prefix, int expectedCloseStatusCode, Matcher reasonMatcher) throws InterruptedException + public void assertCloseInfo(String prefix, int expectedCloseStatusCode, Matcher reasonMatcher) throws InterruptedException { CloseInfo close = closeInfo.get(); assertThat(prefix + " close info", close, Matchers.notNullValue()); diff --git a/jetty-websocket/websocket-tests/src/main/java/org/eclipse/jetty/websocket/tests/UntrustedWSClient.java b/jetty-websocket/websocket-tests/src/main/java/org/eclipse/jetty/websocket/tests/UntrustedWSClient.java index 783096e195d..326395eb5ca 100644 --- a/jetty-websocket/websocket-tests/src/main/java/org/eclipse/jetty/websocket/tests/UntrustedWSClient.java +++ b/jetty-websocket/websocket-tests/src/main/java/org/eclipse/jetty/websocket/tests/UntrustedWSClient.java @@ -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)); + } } diff --git a/jetty-websocket/websocket-tests/src/main/java/org/eclipse/jetty/websocket/tests/WSServer.java b/jetty-websocket/websocket-tests/src/main/java/org/eclipse/jetty/websocket/tests/WSServer.java new file mode 100644 index 00000000000..70b707b841f --- /dev/null +++ b/jetty-websocket/websocket-tests/src/main/java/org/eclipse/jetty/websocket/tests/WSServer.java @@ -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. + *

+ * 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); + } + } + } +} diff --git a/jetty-websocket/websocket-tests/src/main/java/org/eclipse/jetty/websocket/tests/servlets/EchoServlet.java b/jetty-websocket/websocket-tests/src/main/java/org/eclipse/jetty/websocket/tests/servlets/EchoServlet.java new file mode 100644 index 00000000000..241d432674f --- /dev/null +++ b/jetty-websocket/websocket-tests/src/main/java/org/eclipse/jetty/websocket/tests/servlets/EchoServlet.java @@ -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); + } +} diff --git a/jetty-websocket/websocket-tests/src/main/java/org/eclipse/jetty/websocket/tests/servlets/EchoSocket.java b/jetty-websocket/websocket-tests/src/main/java/org/eclipse/jetty/websocket/tests/servlets/EchoSocket.java new file mode 100644 index 00000000000..5b93a259557 --- /dev/null +++ b/jetty-websocket/websocket-tests/src/main/java/org/eclipse/jetty/websocket/tests/servlets/EchoSocket.java @@ -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); + } + } +} diff --git a/jetty-websocket/websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/server/AnnotatedMaxMessageSizeTest.java b/jetty-websocket/websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/server/AnnotatedMaxMessageSizeTest.java new file mode 100644 index 00000000000..b6bb35cce93 --- /dev/null +++ b/jetty-websocket/websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/server/AnnotatedMaxMessageSizeTest.java @@ -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 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 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()); + } +} diff --git a/jetty-websocket/websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/server/ChromeTest.java b/jetty-websocket/websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/server/ChromeTest.java new file mode 100644 index 00000000000..e3ee268bf29 --- /dev/null +++ b/jetty-websocket/websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/server/ChromeTest.java @@ -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 clientConnectFuture = client.connect(clientSocket, wsUri, upgradeRequest); + + Session clientSession = clientConnectFuture.get(Defaults.CONNECT_TIMEOUT_MS, TimeUnit.MILLISECONDS); + List 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(); + } +} diff --git a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/DecoratorsLegacyTest.java b/jetty-websocket/websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/server/DecoratorsLegacyTest.java similarity index 69% rename from jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/DecoratorsLegacyTest.java rename to jetty-websocket/websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/server/DecoratorsLegacyTest.java index 5c2bfce2190..87cb8e9cb57 100644 --- a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/DecoratorsLegacyTest.java +++ b/jetty-websocket/websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/server/DecoratorsLegacyTest.java @@ -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 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 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 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(); } } diff --git a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/DecoratorsTest.java b/jetty-websocket/websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/server/DecoratorsTest.java similarity index 72% rename from jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/DecoratorsTest.java rename to jetty-websocket/websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/server/DecoratorsTest.java index dfdfd0bd4c3..a0dfcf3d5a3 100644 --- a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/DecoratorsTest.java +++ b/jetty-websocket/websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/server/DecoratorsTest.java @@ -16,35 +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.Ignore; +import org.junit.Rule; import org.junit.Test; +import org.junit.rules.TestName; @Ignore public class DecoratorsTest @@ -146,36 +153,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 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 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(); } } diff --git a/jetty-websocket/websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/server/FirefoxTest.java b/jetty-websocket/websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/server/FirefoxTest.java new file mode 100644 index 00000000000..8980e33a7d2 --- /dev/null +++ b/jetty-websocket/websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/server/FirefoxTest.java @@ -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 clientConnectFuture = client.connect(clientSocket, wsUri, upgradeRequest); + + Session clientSession = clientConnectFuture.get(Defaults.CONNECT_TIMEOUT_MS, TimeUnit.MILLISECONDS); + List 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(); + + } +} diff --git a/jetty-websocket/websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/server/FragmentExtensionTest.java b/jetty-websocket/websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/server/FragmentExtensionTest.java new file mode 100644 index 00000000000..e82ccd775be --- /dev/null +++ b/jetty-websocket/websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/server/FragmentExtensionTest.java @@ -0,0 +1,133 @@ +// +// ======================================================================== +// Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== +// + +package org.eclipse.jetty.websocket.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 clientConnectFuture = client.connect(clientSocket, wsUri, upgradeRequest); + + Session clientSession = clientConnectFuture.get(Defaults.CONNECT_TIMEOUT_MS, TimeUnit.MILLISECONDS); + List 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(); + } +} diff --git a/jetty-websocket/websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/server/IdentityExtensionTest.java b/jetty-websocket/websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/server/IdentityExtensionTest.java new file mode 100644 index 00000000000..269b63f6a86 --- /dev/null +++ b/jetty-websocket/websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/server/IdentityExtensionTest.java @@ -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 clientConnectFuture = client.connect(clientSocket, wsUri, upgradeRequest); + + Session clientSession = clientConnectFuture.get(Defaults.CONNECT_TIMEOUT_MS, TimeUnit.MILLISECONDS); + List 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(); + } +} diff --git a/jetty-websocket/websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/server/IdleTimeoutTest.java b/jetty-websocket/websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/server/IdleTimeoutTest.java new file mode 100644 index 00000000000..9e58086b40d --- /dev/null +++ b/jetty-websocket/websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/server/IdleTimeoutTest.java @@ -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 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(); + } +} diff --git a/jetty-websocket/websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/server/InfoContextAltAttributeListener.java b/jetty-websocket/websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/server/InfoContextAltAttributeListener.java new file mode 100644 index 00000000000..8e1eea0d63f --- /dev/null +++ b/jetty-websocket/websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/server/InfoContextAltAttributeListener.java @@ -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(); + } +} diff --git a/jetty-websocket/websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/server/InfoContextAttributeListener.java b/jetty-websocket/websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/server/InfoContextAttributeListener.java new file mode 100644 index 00000000000..c47a5aa40a3 --- /dev/null +++ b/jetty-websocket/websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/server/InfoContextAttributeListener.java @@ -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(); + } +} diff --git a/jetty-websocket/websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/server/InfoContextListener.java b/jetty-websocket/websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/server/InfoContextListener.java new file mode 100644 index 00000000000..7911f33afab --- /dev/null +++ b/jetty-websocket/websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/server/InfoContextListener.java @@ -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(); + } +} diff --git a/jetty-websocket/websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/server/InfoServlet.java b/jetty-websocket/websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/server/InfoServlet.java new file mode 100644 index 00000000000..b203862cd66 --- /dev/null +++ b/jetty-websocket/websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/server/InfoServlet.java @@ -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); + } +} diff --git a/jetty-websocket/websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/server/InfoSocket.java b/jetty-websocket/websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/server/InfoSocket.java new file mode 100644 index 00000000000..aa473958994 --- /dev/null +++ b/jetty-websocket/websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/server/InfoSocket.java @@ -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()); + } +} diff --git a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ManyConnectionsCleanupTest.java b/jetty-websocket/websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/server/ManyConnectionsCleanupTest.java similarity index 56% rename from jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ManyConnectionsCleanupTest.java rename to jetty-websocket/websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/server/ManyConnectionsCleanupTest.java index eaf94b00384..9192b876b0a 100644 --- a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ManyConnectionsCleanupTest.java +++ b/jetty-websocket/websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/server/ManyConnectionsCleanupTest.java @@ -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 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 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 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 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 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 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 clientConnectFuture = client.connect(clientSocket, wsUri, upgradeRequest); + + Session clientSession = clientConnectFuture.get(Defaults.CONNECT_TIMEOUT_MS, TimeUnit.MILLISECONDS); + + clientSession.close(); } + } diff --git a/jetty-websocket/websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/server/MisbehavingClassTest.java b/jetty-websocket/websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/server/MisbehavingClassTest.java new file mode 100644 index 00000000000..3d45957ba07 --- /dev/null +++ b/jetty-websocket/websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/server/MisbehavingClassTest.java @@ -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 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 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 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 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(); + } + } +} diff --git a/jetty-websocket/websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/server/RFC6455Socket.java b/jetty-websocket/websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/server/RFC6455Socket.java new file mode 100644 index 00000000000..c2ed65e9037 --- /dev/null +++ b/jetty-websocket/websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/server/RFC6455Socket.java @@ -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); + } +} diff --git a/jetty-websocket/websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/server/RequestHeadersTest.java b/jetty-websocket/websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/server/RequestHeadersTest.java new file mode 100644 index 00000000000..5b5797f000b --- /dev/null +++ b/jetty-websocket/websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/server/RequestHeadersTest.java @@ -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 clientConnectFuture = client.connect(clientSocket, wsUri, upgradeRequest); + + Session clientSession = clientConnectFuture.get(Defaults.CONNECT_TIMEOUT_MS, TimeUnit.MILLISECONDS); + + List 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(); + } +} diff --git a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/SubProtocolTest.java b/jetty-websocket/websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/server/SubProtocolTest.java similarity index 62% rename from jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/SubProtocolTest.java rename to jetty-websocket/websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/server/SubProtocolTest.java index db0f2306ef8..8f77c0edb92 100644 --- a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/SubProtocolTest.java +++ b/jetty-websocket/websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/server/SubProtocolTest.java @@ -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 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 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(); } } diff --git a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/SuspendResumeTest.java b/jetty-websocket/websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/server/SuspendResumeTest.java similarity index 61% rename from jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/SuspendResumeTest.java rename to jetty-websocket/websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/server/SuspendResumeTest.java index 5464d9d2224..2d6d2329f83 100644 --- a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/SuspendResumeTest.java +++ b/jetty-websocket/websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/server/SuspendResumeTest.java @@ -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 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 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(); } } diff --git a/jetty-websocket/websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/server/TooFastClientTest.java b/jetty-websocket/websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/server/TooFastClientTest.java new file mode 100644 index 00000000000..3781bfb1257 --- /dev/null +++ b/jetty-websocket/websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/server/TooFastClientTest.java @@ -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. + *

+ * 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 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 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(); + } + + +} diff --git a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/WebSocketCloseTest.java b/jetty-websocket/websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/server/WebSocketCloseTest.java similarity index 51% rename from jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/WebSocketCloseTest.java rename to jetty-websocket/websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/server/WebSocketCloseTest.java index 75e7e7e91a6..901193e707b 100644 --- a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/WebSocketCloseTest.java +++ b/jetty-websocket/websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/server/WebSocketCloseTest.java @@ -16,42 +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.Ignore; +import org.junit.Rule; import org.junit.Test; +import org.junit.rules.TestName; /** * Tests various close scenarios @@ -117,7 +119,7 @@ public class WebSocketCloseTest closeSocket = new ContainerSocket(serverFactory); return closeSocket; } - return new RFCSocket(); + return new RFC6455Socket(); } } @@ -208,10 +210,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) @@ -222,28 +242,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 clientConnectFuture = client.connect(clientSocket, wsUri, upgradeRequest); + + Session clientSession = clientConnectFuture.get(Defaults.CONNECT_TIMEOUT_MS, TimeUnit.MILLISECONDS); - // Verify that client got close frame - EventQueue 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(); } /** @@ -255,30 +267,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 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 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(); } /** @@ -293,106 +295,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 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 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 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 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 clientConnectFuture = client.connect(clientSocket, wsUri, upgradeRequest); + + Session clientSession = clientConnectFuture.get(Defaults.CONNECT_TIMEOUT_MS, TimeUnit.MILLISECONDS); + + clientSession.close(); } } diff --git a/jetty-websocket/websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/server/WebSocketInvalidVersionTest.java b/jetty-websocket/websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/server/WebSocketInvalidVersionTest.java new file mode 100644 index 00000000000..338093503a7 --- /dev/null +++ b/jetty-websocket/websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/server/WebSocketInvalidVersionTest.java @@ -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 clientConnectFuture = client.connect(clientSocket, wsUri, upgradeRequest); + // TODO: handle exception? + Assert.fail("Should have handled exception check"); + } +} diff --git a/jetty-websocket/websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/server/WebSocketServerSessionTest.java b/jetty-websocket/websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/server/WebSocketServerSessionTest.java new file mode 100644 index 00000000000..46a8d489517 --- /dev/null +++ b/jetty-websocket/websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/server/WebSocketServerSessionTest.java @@ -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> parameterMap = session.getUpgradeRequest().getParameterMap(); + + int idx = message.indexOf('|'); + String key = message.substring(idx + 1); + List values = parameterMap.get(key); + + if (values == null) + { + sendString(""); + 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 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 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("")); + } +} diff --git a/jetty-websocket/websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/server/WebSocketServletRFCTest.java b/jetty-websocket/websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/server/WebSocketServletRFCTest.java new file mode 100644 index 00000000000..07a298b4d7d --- /dev/null +++ b/jetty-websocket/websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/server/WebSocketServletRFCTest.java @@ -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 RFC 6455 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 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 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. + *

+ * 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 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. + *

+ * 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 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 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()); + } +} diff --git a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/WebSocketUpgradeFilterTest.java b/jetty-websocket/websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/server/WebSocketUpgradeFilterTest.java similarity index 75% rename from jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/WebSocketUpgradeFilterTest.java rename to jetty-websocket/websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/server/WebSocketUpgradeFilterTest.java index f92bdcc9ed1..9abb8258b01 100644 --- a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/WebSocketUpgradeFilterTest.java +++ b/jetty-websocket/websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/server/WebSocketUpgradeFilterTest.java @@ -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 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 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 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 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 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(); } } diff --git a/jetty-websocket/websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/server/jsr356/ConfiguratorTest.java b/jetty-websocket/websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/server/jsr356/ConfiguratorTest.java index 8549adcce14..fa90016d5ed 100644 --- a/jetty-websocket/websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/server/jsr356/ConfiguratorTest.java +++ b/jetty-websocket/websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/server/jsr356/ConfiguratorTest.java @@ -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)