From 88b2c47904e1bece0b0bfb2cac502cb8672d94bf Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Wed, 26 Apr 2017 18:13:56 -0700 Subject: [PATCH] Issue #207 - moved some JSR356 tests to websocket-tests # Conflicts: # jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/DecoderReaderManySmallTest.java # jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/DecoderReaderTest.java # jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/EncoderTest.java --- .../jsr356/DecoderReaderManySmallTest.java | 228 --------- .../websocket/jsr356/DecoderReaderTest.java | 300 ------------ .../jetty/websocket/jsr356/EncoderTest.java | 325 ------------- ...tty.websocket.common.reflect.ArgIdentifier | 1 - jetty-websocket/websocket-tests/pom.xml | 10 + .../tests/AbstractJsrTrackingEndpoint.java | 46 ++ .../tests/AbstractTrackingEndpoint.java | 119 +++++ .../websocket/tests/TrackingEndpoint.java | 86 +--- .../jsr356/DecoderReaderManySmallTest.java | 202 ++++++++ .../websocket/tests/client/jsr356/Quotes.java | 48 ++ .../tests/client/jsr356/QuotesDecoder.java | 73 +++ .../client/jsr356/QuotesDecoderTest.java | 215 +++++++++ .../tests/client/jsr356/QuotesEncoder.java | 52 ++ .../client/jsr356/QuotesEncoderTest.java | 197 ++++++++ .../server/jsr356}/ConfiguratorTest.java | 449 +++++++++--------- .../test/resources/jetty-logging.properties | 16 +- .../src/test/resources/quotes-ben.txt | 4 + .../src/test/resources/quotes-twain.txt | 5 + 18 files changed, 1217 insertions(+), 1159 deletions(-) delete mode 100644 jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/DecoderReaderManySmallTest.java delete mode 100644 jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/DecoderReaderTest.java delete mode 100644 jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/EncoderTest.java delete mode 100644 jetty-websocket/javax-websocket-server-impl/src/main/resources/META-INF/services/org.eclipse.jetty.websocket.common.reflect.ArgIdentifier create mode 100644 jetty-websocket/websocket-tests/src/main/java/org/eclipse/jetty/websocket/tests/AbstractJsrTrackingEndpoint.java create mode 100644 jetty-websocket/websocket-tests/src/main/java/org/eclipse/jetty/websocket/tests/AbstractTrackingEndpoint.java create mode 100644 jetty-websocket/websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/client/jsr356/DecoderReaderManySmallTest.java create mode 100644 jetty-websocket/websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/client/jsr356/Quotes.java create mode 100644 jetty-websocket/websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/client/jsr356/QuotesDecoder.java create mode 100644 jetty-websocket/websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/client/jsr356/QuotesDecoderTest.java create mode 100644 jetty-websocket/websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/client/jsr356/QuotesEncoder.java create mode 100644 jetty-websocket/websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/client/jsr356/QuotesEncoderTest.java rename jetty-websocket/{javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server => websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/server/jsr356}/ConfiguratorTest.java (57%) create mode 100644 jetty-websocket/websocket-tests/src/test/resources/quotes-ben.txt create mode 100644 jetty-websocket/websocket-tests/src/test/resources/quotes-twain.txt diff --git a/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/DecoderReaderManySmallTest.java b/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/DecoderReaderManySmallTest.java deleted file mode 100644 index 0683cb5d839..00000000000 --- a/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/DecoderReaderManySmallTest.java +++ /dev/null @@ -1,228 +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.jsr356; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.Reader; -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; - -import javax.websocket.ClientEndpoint; -import javax.websocket.CloseReason; -import javax.websocket.ContainerProvider; -import javax.websocket.DecodeException; -import javax.websocket.Decoder; -import javax.websocket.EndpointConfig; -import javax.websocket.OnClose; -import javax.websocket.OnMessage; -import javax.websocket.WebSocketContainer; - -import org.eclipse.jetty.toolchain.test.EventQueue; -import org.eclipse.jetty.toolchain.test.TestTracker; -import org.eclipse.jetty.util.component.LifeCycle; -import org.eclipse.jetty.util.log.Log; -import org.eclipse.jetty.util.log.Logger; -import org.eclipse.jetty.websocket.common.frames.TextFrame; -import org.eclipse.jetty.websocket.common.test.XBlockheadServer; -import org.eclipse.jetty.websocket.common.test.IBlockheadServerConnection; -import org.junit.After; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Ignore; -import org.junit.Rule; -import org.junit.Test; - -@Ignore("Not working atm") -public class DecoderReaderManySmallTest -{ - public static class EventId - { - public int eventId; - } - - public static class EventIdDecoder implements Decoder.TextStream - { - @Override - public void init(EndpointConfig config) - { - } - - @Override - public void destroy() - { - } - - @Override - public EventId decode(Reader reader) throws DecodeException, IOException - { - EventId id = new EventId(); - try (BufferedReader buf = new BufferedReader(reader)) - { - String line; - while ((line = buf.readLine()) != null) - { - id.eventId = Integer.parseInt(line); - } - } - return id; - } - } - - @ClientEndpoint(decoders = { EventIdDecoder.class }) - public static class EventIdSocket - { - public EventQueue messageQueue = new EventQueue<>(); - private CountDownLatch closeLatch = new CountDownLatch(1); - - @OnClose - public void onClose(CloseReason close) - { - closeLatch.countDown(); - } - - @OnMessage - public void onMessage(EventId msg) - { - messageQueue.add(msg); - } - - public void awaitClose() throws InterruptedException - { - closeLatch.await(4,TimeUnit.SECONDS); - } - } - - private static class EventIdServer implements Runnable - { - private XBlockheadServer server; - private IBlockheadServerConnection sconnection; - private CountDownLatch connectLatch = new CountDownLatch(1); - - public EventIdServer(XBlockheadServer server) - { - this.server = server; - } - - @Override - public void run() - { - try - { - sconnection = server.accept(); - sconnection.setSoTimeout(60000); - sconnection.upgrade(); - } - catch (Exception e) - { - LOG.warn(e); - } - finally - { - connectLatch.countDown(); - } - } - - public void writeSequentialIds(int from, int to) throws IOException - { - for (int id = from; id < to; id++) - { - TextFrame frame = new TextFrame(); - frame.setPayload(Integer.toString(id)); - sconnection.write(frame); - } - } - - public void close() throws IOException - { - sconnection.close(); - } - - public void awaitConnect() throws InterruptedException - { - connectLatch.await(1,TimeUnit.SECONDS); - } - } - - private static final Logger LOG = Log.getLogger(DecoderReaderManySmallTest.class); - - @Rule - public TestTracker tt = new TestTracker(); - - private XBlockheadServer server; - private WebSocketContainer client; - - @Before - public void initClient() - { - client = ContainerProvider.getWebSocketContainer(); - } - - @After - public void stopClient() throws Exception - { - ((LifeCycle)client).stop(); - } - - @Before - public void startServer() throws Exception - { - server = new XBlockheadServer(); - server.start(); - } - - @After - public void stopServer() throws Exception - { - server.stop(); - } - - @Test - public void testManyIds() throws Exception - { - EventIdSocket ids = new EventIdSocket(); - EventIdServer idserver = new EventIdServer(server); - new Thread(idserver).start(); - client.connectToServer(ids,server.getWsUri()); - idserver.awaitConnect(); - int from = 1000; - int to = 2000; - idserver.writeSequentialIds(from,to); - idserver.close(); - int count = from - to; - ids.messageQueue.awaitEventCount(count,4,TimeUnit.SECONDS); - ids.awaitClose(); - // collect seen ids - List seen = new ArrayList<>(); - for(EventId id: ids.messageQueue) - { - // validate that ids don't repeat. - Assert.assertFalse("Already saw ID: " + id.eventId, seen.contains(id.eventId)); - seen.add(id.eventId); - } - - // validate that all expected ids have been seen (order is irrelevant here) - for(int expected=from; expected quotes = new ArrayList<>(); - - public String getAuthor() - { - return author; - } - - public void setAuthor(String author) - { - this.author = author; - } - - public List getQuotes() - { - return quotes; - } - - public void addQuote(String quote) - { - quotes.add(quote); - } - } - - public static class QuotesDecoder implements Decoder.TextStream - { - @Override - public void init(EndpointConfig config) - { - } - - @Override - public void destroy() - { - } - - @Override - public Quotes decode(Reader reader) throws DecodeException, IOException - { - Quotes quotes = new Quotes(); - try (BufferedReader buf = new BufferedReader(reader)) - { - String line; - while ((line = buf.readLine()) != null) - { - switch (line.charAt(0)) - { - case 'a': - quotes.setAuthor(line.substring(2)); - break; - case 'q': - quotes.addQuote(line.substring(2)); - break; - } - } - } - return quotes; - } - } - - @ClientEndpoint(decoders = { QuotesDecoder.class }) - public static class QuotesSocket - { - public EventQueue messageQueue = new EventQueue<>(); - private CountDownLatch closeLatch = new CountDownLatch(1); - - @OnClose - public void onClose(CloseReason close) - { - closeLatch.countDown(); - } - - @OnMessage - public synchronized void onMessage(Quotes msg) - { - Integer h=hashCode(); - messageQueue.add(msg); - System.out.printf("%x: Quotes from: %s%n",h,msg.author); - for (String quote : msg.quotes) - { - System.out.printf("%x: - %s%n",h,quote); - } - } - - public void awaitClose() throws InterruptedException - { - closeLatch.await(4,TimeUnit.SECONDS); - } - } - - private static class QuoteServer implements Runnable - { - private XBlockheadServer server; - private IBlockheadServerConnection sconnection; - private CountDownLatch connectLatch = new CountDownLatch(1); - - public QuoteServer(XBlockheadServer server) - { - this.server = server; - } - - @Override - public void run() - { - try - { - sconnection = server.accept(); - sconnection.setSoTimeout(60000); - sconnection.upgrade(); - } - catch (Exception e) - { - LOG.warn(e); - } - finally - { - connectLatch.countDown(); - } - } - - public void writeQuotes(String filename) throws IOException - { - // read file - File qfile = MavenTestingUtils.getTestResourceFile(filename); - List lines = new ArrayList<>(); - try (FileReader reader = new FileReader(qfile); BufferedReader buf = new BufferedReader(reader)) - { - String line; - while ((line = buf.readLine()) != null) - { - lines.add(line); - } - } - // write file out, each line on a separate frame, but as - // 1 whole message - for (int i = 0; i < lines.size(); i++) - { - WebSocketFrame frame; - if (i == 0) - { - frame = new TextFrame(); - } - else - { - frame = new ContinuationFrame(); - } - frame.setFin((i >= (lines.size() - 1))); - frame.setPayload(BufferUtil.toBuffer(lines.get(i) + "\n")); - sconnection.write(frame); - } - } - - public void close() throws IOException - { - sconnection.close(); - } - - public void awaitConnect() throws InterruptedException - { - connectLatch.await(1,TimeUnit.SECONDS); - } - } - - private static final Logger LOG = Log.getLogger(DecoderReaderTest.class); - - @Rule - public TestTracker tt = new TestTracker(); - - private XBlockheadServer server; - private WebSocketContainer client; - - @Before - public void initClient() - { - client = ContainerProvider.getWebSocketContainer(); - } - - @After - public void stopClient() throws Exception - { - ((LifeCycle)client).stop(); - } - - @Before - public void startServer() throws Exception - { - server = new XBlockheadServer(); - server.start(); - } - - @After - public void stopServer() throws Exception - { - server.stop(); - } - - // TODO analyse and fix - @Ignore - @Test - public void testSingleQuotes() throws Exception - { - QuotesSocket quoter = new QuotesSocket(); - QuoteServer qserver = new QuoteServer(server); - new Thread(qserver).start(); - client.connectToServer(quoter,server.getWsUri()); - qserver.awaitConnect(); - qserver.writeQuotes("quotes-ben.txt"); - quoter.messageQueue.awaitEventCount(1,1000,TimeUnit.MILLISECONDS); - qserver.close(); - quoter.awaitClose(); - Quotes quotes = quoter.messageQueue.poll(); - Assert.assertThat("Quotes Author",quotes.author,is("Benjamin Franklin")); - Assert.assertThat("Quotes Count",quotes.quotes.size(),is(3)); - } - - // TODO analyse and fix - @Test - @Ignore ("Quotes appear to be able to arrive in any order?") - public void testTwoQuotes() throws Exception - { - QuotesSocket quoter = new QuotesSocket(); - QuoteServer qserver = new QuoteServer(server); - new Thread(qserver).start(); - client.connectToServer(quoter,server.getWsUri()); - qserver.awaitConnect(); - qserver.writeQuotes("quotes-ben.txt"); - qserver.writeQuotes("quotes-twain.txt"); - quoter.messageQueue.awaitEventCount(2,1000,TimeUnit.MILLISECONDS); - qserver.close(); - quoter.awaitClose(); - Quotes quotes = quoter.messageQueue.poll(); - Assert.assertThat("Quotes Author",quotes.author,is("Benjamin Franklin")); - Assert.assertThat("Quotes Count",quotes.quotes.size(),is(3)); - } -} diff --git a/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/EncoderTest.java b/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/EncoderTest.java deleted file mode 100644 index e35a03f3a4c..00000000000 --- a/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/EncoderTest.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.jsr356; - -import static org.hamcrest.Matchers.containsString; - -import java.io.BufferedReader; -import java.io.File; -import java.io.FileReader; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; - -import javax.websocket.ClientEndpointConfig; -import javax.websocket.ContainerProvider; -import javax.websocket.EncodeException; -import javax.websocket.Encoder; -import javax.websocket.Endpoint; -import javax.websocket.EndpointConfig; -import javax.websocket.MessageHandler; -import javax.websocket.Session; -import javax.websocket.WebSocketContainer; - -import org.eclipse.jetty.toolchain.test.EventQueue; -import org.eclipse.jetty.toolchain.test.MavenTestingUtils; -import org.eclipse.jetty.toolchain.test.TestTracker; -import org.eclipse.jetty.util.component.LifeCycle; -import org.eclipse.jetty.util.log.Log; -import org.eclipse.jetty.util.log.Logger; -import org.eclipse.jetty.websocket.common.test.XBlockheadServer; -import org.eclipse.jetty.websocket.common.test.IBlockheadServerConnection; -import org.junit.After; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; - -public class EncoderTest -{ - private static class EchoServer implements Runnable - { - private Thread thread; - private XBlockheadServer server; - private IBlockheadServerConnection sconnection; - private CountDownLatch connectLatch = new CountDownLatch(1); - - public EchoServer(XBlockheadServer server) - { - this.server = server; - } - - @Override - public void run() - { - try - { - sconnection = server.accept(); - sconnection.setSoTimeout(60000); - sconnection.upgrade(); - sconnection.startEcho(); - } - catch (Exception e) - { - LOG.warn(e); - } - finally - { - connectLatch.countDown(); - } - } - - public void start() - { - this.thread = new Thread(this,"EchoServer"); - this.thread.start(); - } - - public void stop() - { - if (this.sconnection != null) - { - this.sconnection.stopEcho(); - try - { - this.sconnection.close(); - } - catch (IOException ignore) - { - /* ignore */ - } - } - } - } - - public static class Quotes - { - private String author; - private List quotes = new ArrayList<>(); - - public void addQuote(String quote) - { - quotes.add(quote); - } - - public String getAuthor() - { - return author; - } - - public List getQuotes() - { - return quotes; - } - - public void setAuthor(String author) - { - this.author = author; - } - } - - public static class QuotesEncoder implements Encoder.Text - { - @Override - public void destroy() - { - } - - @Override - public String encode(Quotes q) throws EncodeException - { - StringBuilder buf = new StringBuilder(); - buf.append("Author: ").append(q.getAuthor()); - buf.append(System.lineSeparator()); - for (String quote : q.quotes) - { - buf.append("Quote: ").append(quote); - buf.append(System.lineSeparator()); - } - return buf.toString(); - } - - @Override - public void init(EndpointConfig config) - { - } - } - - public static class QuotesSocket extends Endpoint implements MessageHandler.Whole - { - private Session session; - private EventQueue messageQueue = new EventQueue<>(); - - @Override - public void onMessage(String message) - { - messageQueue.add(message); - } - - @Override - public void onOpen(Session session, EndpointConfig config) - { - this.session = session; - this.session.addMessageHandler(this); - } - - public void write(Quotes quotes) throws IOException, EncodeException - { - if (LOG.isDebugEnabled()) - LOG.debug("Writing Quotes: {}",quotes); - this.session.getBasicRemote().sendObject(quotes); - } - } - - private static final Logger LOG = Log.getLogger(EncoderTest.class); - - @Rule - public TestTracker tt = new TestTracker(); - private XBlockheadServer server; - - private WebSocketContainer client; - - private void assertReceivedQuotes(String result, Quotes quotes) - { - Assert.assertThat("Quote Author",result,containsString("Author: " + quotes.getAuthor())); - for (String quote : quotes.quotes) - { - Assert.assertThat("Quote",result,containsString("Quote: " + quote)); - } - } - - @SuppressWarnings("Duplicates") - private Quotes getQuotes(String filename) throws IOException - { - Quotes quotes = new Quotes(); - - // read file - File qfile = MavenTestingUtils.getTestResourceFile(filename); - try (FileReader reader = new FileReader(qfile); BufferedReader buf = new BufferedReader(reader)) - { - String line; - while ((line = buf.readLine()) != null) - { - switch (line.charAt(0)) - { - case 'a': - quotes.setAuthor(line.substring(2)); - break; - case 'q': - quotes.addQuote(line.substring(2)); - break; - } - } - } - - return quotes; - } - - @Before - public void initClient() - { - client = ContainerProvider.getWebSocketContainer(); - } - - @After - public void stopClient() throws Exception - { - ((LifeCycle)client).stop(); - } - - @Before - public void startServer() throws Exception - { - server = new XBlockheadServer(); - server.start(); - } - - @After - public void stopServer() throws Exception - { - server.stop(); - } - - @Test - public void testSingleQuotes() throws Exception - { - EchoServer echoServer = new EchoServer(server); - try - { - echoServer.start(); - - QuotesSocket quoter = new QuotesSocket(); - - ClientEndpointConfig.Builder builder = ClientEndpointConfig.Builder.create(); - List> encoders = new ArrayList<>(); - encoders.add(QuotesEncoder.class); - builder.encoders(encoders); - ClientEndpointConfig cec = builder.build(); - client.connectToServer(quoter,cec,server.getWsUri()); - - Quotes ben = getQuotes("quotes-ben.txt"); - quoter.write(ben); - - quoter.messageQueue.awaitEventCount(1,1000,TimeUnit.MILLISECONDS); - - String result = quoter.messageQueue.poll(); - assertReceivedQuotes(result,ben); - } - finally - { - echoServer.stop(); - } - } - - @Test - public void testTwoQuotes() throws Exception - { - EchoServer echoServer = new EchoServer(server); - try - { - echoServer.start(); - - QuotesSocket quoter = new QuotesSocket(); - ClientEndpointConfig.Builder builder = ClientEndpointConfig.Builder.create(); - List> encoders = new ArrayList<>(); - encoders.add(QuotesEncoder.class); - builder.encoders(encoders); - ClientEndpointConfig cec = builder.build(); - client.connectToServer(quoter,cec,server.getWsUri()); - - Quotes ben = getQuotes("quotes-ben.txt"); - Quotes twain = getQuotes("quotes-twain.txt"); - quoter.write(ben); - quoter.write(twain); - - quoter.messageQueue.awaitEventCount(2,1000,TimeUnit.MILLISECONDS); - - String result = quoter.messageQueue.poll(); - assertReceivedQuotes(result,ben); - result = quoter.messageQueue.poll(); - assertReceivedQuotes(result,twain); - } - finally - { - echoServer.stop(); - } - } -} diff --git a/jetty-websocket/javax-websocket-server-impl/src/main/resources/META-INF/services/org.eclipse.jetty.websocket.common.reflect.ArgIdentifier b/jetty-websocket/javax-websocket-server-impl/src/main/resources/META-INF/services/org.eclipse.jetty.websocket.common.reflect.ArgIdentifier deleted file mode 100644 index 120238b1bf3..00000000000 --- a/jetty-websocket/javax-websocket-server-impl/src/main/resources/META-INF/services/org.eclipse.jetty.websocket.common.reflect.ArgIdentifier +++ /dev/null @@ -1 +0,0 @@ -org.eclipse.jetty.websocket.jsr356.server.PathParamArgIdentifier diff --git a/jetty-websocket/websocket-tests/pom.xml b/jetty-websocket/websocket-tests/pom.xml index 3a6c868793d..bb36152ae7d 100644 --- a/jetty-websocket/websocket-tests/pom.xml +++ b/jetty-websocket/websocket-tests/pom.xml @@ -20,6 +20,11 @@ websocket-api ${project.version} + + org.eclipse.jetty.websocket + javax-websocket-client-impl + ${project.version} + org.eclipse.jetty.websocket websocket-client @@ -30,6 +35,11 @@ websocket-server ${project.version} + + org.eclipse.jetty.websocket + javax-websocket-server-impl + ${project.version} + org.eclipse.jetty jetty-util diff --git a/jetty-websocket/websocket-tests/src/main/java/org/eclipse/jetty/websocket/tests/AbstractJsrTrackingEndpoint.java b/jetty-websocket/websocket-tests/src/main/java/org/eclipse/jetty/websocket/tests/AbstractJsrTrackingEndpoint.java new file mode 100644 index 00000000000..ef942b3c308 --- /dev/null +++ b/jetty-websocket/websocket-tests/src/main/java/org/eclipse/jetty/websocket/tests/AbstractJsrTrackingEndpoint.java @@ -0,0 +1,46 @@ +// +// ======================================================================== +// 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 javax.websocket.CloseReason; +import javax.websocket.EndpointConfig; +import javax.websocket.OnClose; +import javax.websocket.OnOpen; +import javax.websocket.Session; + +@SuppressWarnings("unused") +public abstract class AbstractJsrTrackingEndpoint extends AbstractTrackingEndpoint +{ + public AbstractJsrTrackingEndpoint(String id) + { + super(id); + } + + @OnOpen + public void onOpen(Session session, EndpointConfig config) + { + super.onWSOpen(session); + } + + @OnClose + public void onClose(CloseReason closeReason) + { + super.onWSClose(closeReason.getCloseCode().getCode(), closeReason.getReasonPhrase()); + } +} 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 new file mode 100644 index 00000000000..48fce847183 --- /dev/null +++ b/jetty-websocket/websocket-tests/src/main/java/org/eclipse/jetty/websocket/tests/AbstractTrackingEndpoint.java @@ -0,0 +1,119 @@ +// +// ======================================================================== +// 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.notNullValue; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; + +import org.eclipse.jetty.util.log.Log; +import org.eclipse.jetty.util.log.Logger; +import org.eclipse.jetty.websocket.common.CloseInfo; +import org.hamcrest.Matcher; +import org.hamcrest.Matchers; + +public abstract class AbstractTrackingEndpoint +{ + public final Logger LOG; + + public T session; + + public CountDownLatch openLatch = new CountDownLatch(1); + public CountDownLatch closeLatch = new CountDownLatch(1); + public AtomicReference closeInfo = new AtomicReference<>(); + public AtomicReference error = new AtomicReference<>(); + + public AbstractTrackingEndpoint(String id) + { + LOG = Log.getLogger(this.getClass().getName() + "." + id); + } + + public void assertCloseInfo(String prefix, int expectedCloseStatusCode, Matcher reasonMatcher) throws InterruptedException + { + CloseInfo close = closeInfo.get(); + assertThat(prefix + " close info", close, Matchers.notNullValue()); + assertThat(prefix + " received close code", close.getStatusCode(), Matchers.is(expectedCloseStatusCode)); + assertThat(prefix + " received close reason", close.getReason(), reasonMatcher); + } + + public void assertErrorEvent(String prefix, Matcher throwableMatcher, Matcher messageMatcher) + { + assertThat(prefix + " error event type", error.get(), throwableMatcher); + assertThat(prefix + " error event message", error.get().getMessage(), messageMatcher); + } + + public void assertNoErrorEvents(String prefix) + { + assertTrue(prefix + " error event should not have occurred", error.get() == null); + } + + public void assertNotClosed(String prefix) + { + assertTrue(prefix + " close event should not have occurred", closeLatch.getCount() > 0); + } + + public void assertNotOpened(String prefix) + { + assertTrue(prefix + " open event should not have occurred", openLatch.getCount() > 0); + } + + public void awaitCloseEvent(String prefix) throws InterruptedException + { + assertTrue(prefix + " onClose event", closeLatch.await(Defaults.CLOSE_EVENT_TIMEOUT_MS, TimeUnit.MILLISECONDS)); + } + + public void awaitOpenEvent(String prefix) throws InterruptedException + { + assertTrue(prefix + " onOpen event", openLatch.await(Defaults.OPEN_EVENT_TIMEOUT_MS, TimeUnit.MILLISECONDS)); + } + + protected void onWSOpen(T session) + { + this.session = session; + if (LOG.isDebugEnabled()) + { + LOG.debug("onWSOpen()"); + } + this.openLatch.countDown(); + } + + protected void onWSClose(int statusCode, String reason) + { + CloseInfo close = new CloseInfo(statusCode, reason); + boolean closeTracked = closeInfo.compareAndSet(null, close); + this.closeLatch.countDown(); + assertTrue("Close only happened once", closeTracked); + } + + protected void onWSError(Throwable cause) + { + assertThat("Error must have value", cause, notNullValue()); + if (error.compareAndSet(null, cause) == false) + { + LOG.warn("onError should only happen once - Original Cause", error.get()); + LOG.warn("onError should only happen once - Extra/Excess Cause", cause); + fail("onError should only happen once!"); + } + } +} diff --git a/jetty-websocket/websocket-tests/src/main/java/org/eclipse/jetty/websocket/tests/TrackingEndpoint.java b/jetty-websocket/websocket-tests/src/main/java/org/eclipse/jetty/websocket/tests/TrackingEndpoint.java index 781a1a2fec5..53851b33e9e 100644 --- a/jetty-websocket/websocket-tests/src/main/java/org/eclipse/jetty/websocket/tests/TrackingEndpoint.java +++ b/jetty-websocket/websocket-tests/src/main/java/org/eclipse/jetty/websocket/tests/TrackingEndpoint.java @@ -19,23 +19,14 @@ package org.eclipse.jetty.websocket.tests; import static org.hamcrest.CoreMatchers.instanceOf; -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.CoreMatchers.notNullValue; import static org.junit.Assert.assertThat; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; import java.nio.ByteBuffer; import java.util.concurrent.BlockingQueue; -import java.util.concurrent.CountDownLatch; import java.util.concurrent.LinkedBlockingDeque; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicReference; import org.eclipse.jetty.io.EndPoint; 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.RemoteEndpoint; import org.eclipse.jetty.websocket.api.Session; import org.eclipse.jetty.websocket.api.UpgradeRequest; @@ -43,75 +34,23 @@ import org.eclipse.jetty.websocket.api.UpgradeResponse; import org.eclipse.jetty.websocket.api.WebSocketFrameListener; import org.eclipse.jetty.websocket.api.WebSocketListener; import org.eclipse.jetty.websocket.api.extensions.Frame; -import org.eclipse.jetty.websocket.common.CloseInfo; import org.eclipse.jetty.websocket.common.LogicalConnection; import org.eclipse.jetty.websocket.common.WebSocketFrame; import org.eclipse.jetty.websocket.common.WebSocketSession; import org.eclipse.jetty.websocket.common.io.AbstractWebSocketConnection; -import org.hamcrest.Matcher; -import org.hamcrest.Matchers; -public class TrackingEndpoint implements WebSocketListener, WebSocketFrameListener +public class TrackingEndpoint extends AbstractTrackingEndpoint implements WebSocketListener, WebSocketFrameListener { - public final Logger LOG; - - public CountDownLatch openLatch = new CountDownLatch(1); - public UpgradeRequest openUpgradeRequest; public UpgradeResponse openUpgradeResponse; - public CountDownLatch closeLatch = new CountDownLatch(1); - public AtomicReference closeInfo = new AtomicReference<>(); - public AtomicReference error = new AtomicReference<>(); - + public BlockingQueue framesQueue = new LinkedBlockingDeque<>(); public BlockingQueue messageQueue = new LinkedBlockingDeque<>(); public BlockingQueue bufferQueue = new LinkedBlockingDeque<>(); - public BlockingQueue framesQueue = new LinkedBlockingDeque<>(); - - public WebSocketSession session; public TrackingEndpoint(String id) { - LOG = Log.getLogger(this.getClass().getName() + "." + id); - } - - public void assertCloseInfo(String prefix, int expectedCloseStatusCode, Matcher reasonMatcher) throws InterruptedException - { - CloseInfo close = closeInfo.get(); - assertThat(prefix + " close info", close, Matchers.notNullValue()); - assertThat(prefix + " received close code", close.getStatusCode(), Matchers.is(expectedCloseStatusCode)); - assertThat(prefix + " received close reason", close.getReason(), reasonMatcher); - } - - public void assertNotOpened(String prefix) - { - assertTrue(prefix + " open event should not have occurred", openLatch.getCount() > 0); - } - - public void assertNotClosed(String prefix) - { - assertTrue(prefix + " close event should not have occurred", closeLatch.getCount() > 0); - } - - public void assertNoErrorEvents(String prefix) - { - assertTrue(prefix + " error event should not have occurred", error.get() == null); - } - - public void assertErrorEvent(String prefix, Matcher throwableMatcher, Matcher messageMatcher) - { - assertThat(prefix + " error event type", error.get(), throwableMatcher); - assertThat(prefix + " error event message", error.get().getMessage(), messageMatcher); - } - - public void awaitOpenEvent(String prefix) throws InterruptedException - { - assertTrue(prefix + " onOpen event", openLatch.await(Defaults.OPEN_EVENT_TIMEOUT_MS, TimeUnit.MILLISECONDS)); - } - - public void awaitCloseEvent(String prefix) throws InterruptedException - { - assertTrue(prefix + " onClose event", closeLatch.await(Defaults.CLOSE_EVENT_TIMEOUT_MS, TimeUnit.MILLISECONDS)); + super(id); } public void close(int statusCode, String reason) @@ -138,35 +77,22 @@ public class TrackingEndpoint implements WebSocketListener, WebSocketFrameListen @Override public void onWebSocketClose(int statusCode, String reason) { - this.closeLatch.countDown(); - CloseInfo close = new CloseInfo(statusCode, reason); - assertThat("Close only happened once", closeInfo.compareAndSet(null, close), is(true)); + super.onWSClose(statusCode, reason); } @Override public void onWebSocketConnect(Session session) { assertThat("Session type", session, instanceOf(WebSocketSession.class)); - this.session = (WebSocketSession) session; + super.onWSOpen((WebSocketSession) session); this.openUpgradeRequest = session.getUpgradeRequest(); this.openUpgradeResponse = session.getUpgradeResponse(); - if (LOG.isDebugEnabled()) - { - LOG.debug("onWebSocketConnect()"); - } - this.openLatch.countDown(); } @Override public void onWebSocketError(Throwable cause) { - assertThat("Error must have value", cause, notNullValue()); - if (error.compareAndSet(null, cause) == false) - { - LOG.warn("onError should only happen once - Original Cause", error.get()); - LOG.warn("onError should only happen once - Extra/Excess Cause", cause); - fail("onError should only happen once!"); - } + super.onWSError(cause); } @Override diff --git a/jetty-websocket/websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/client/jsr356/DecoderReaderManySmallTest.java b/jetty-websocket/websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/client/jsr356/DecoderReaderManySmallTest.java new file mode 100644 index 00000000000..69a25a32faf --- /dev/null +++ b/jetty-websocket/websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/client/jsr356/DecoderReaderManySmallTest.java @@ -0,0 +1,202 @@ +// +// ======================================================================== +// 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.client.jsr356; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.Reader; +import java.net.URI; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingDeque; +import java.util.concurrent.TimeUnit; + +import javax.websocket.ClientEndpoint; +import javax.websocket.ContainerProvider; +import javax.websocket.DecodeException; +import javax.websocket.Decoder; +import javax.websocket.EndpointConfig; +import javax.websocket.OnMessage; +import javax.websocket.Session; +import javax.websocket.WebSocketContainer; + +import org.eclipse.jetty.websocket.api.BatchMode; +import org.eclipse.jetty.websocket.api.WebSocketBehavior; +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.tests.AbstractJsrTrackingEndpoint; +import org.eclipse.jetty.websocket.tests.UntrustedWSEndpoint; +import org.eclipse.jetty.websocket.tests.UntrustedWSServer; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TestName; + +public class DecoderReaderManySmallTest +{ + public static class EventId + { + public int eventId; + } + + public static class EventIdDecoder implements Decoder.TextStream + { + @Override + public void init(EndpointConfig config) + { + } + + @Override + public void destroy() + { + } + + @Override + public EventId decode(Reader reader) throws DecodeException, IOException + { + EventId id = new EventId(); + try (BufferedReader buf = new BufferedReader(reader)) + { + String line; + while ((line = buf.readLine()) != null) + { + id.eventId = Integer.parseInt(line); + } + } + return id; + } + } + + @ClientEndpoint(decoders = EventIdDecoder.class, subprotocols = "eventids") + public static class EventIdSocket extends AbstractJsrTrackingEndpoint + { + public BlockingQueue messageQueue = new LinkedBlockingDeque<>(); + + public EventIdSocket(String id) + { + super(id); + } + + @SuppressWarnings("unused") + @OnMessage + public void onMessage(EventId msg) + { + messageQueue.offer(msg); + } + } + + public static class EventIdServerCreator implements WebSocketCreator + { + @Override + public Object createWebSocket(ServletUpgradeRequest req, ServletUpgradeResponse resp) + { + EventIdServerEndpoint endpoint = new EventIdServerEndpoint(WebSocketBehavior.SERVER.name()); + resp.setAcceptedSubProtocol("eventids"); + return endpoint; + } + } + + private static class EventIdServerEndpoint extends UntrustedWSEndpoint + { + public EventIdServerEndpoint(String id) + { + super(id); + } + + @Override + public void onWebSocketText(String text) + { + super.onWebSocketText(text); + + if (text.startsWith("seq|")) + { + String parts[] = text.split("\\|"); + int from = Integer.parseInt(parts[1]); + int to = Integer.parseInt(parts[2]); + + session.getRemote().setBatchMode(BatchMode.OFF); + + for (int id = from; id < to; id++) + { + session.getRemote().sendStringByFuture(Integer.toString(id)); + } + } + } + } + + @Rule + public TestName testname = new TestName(); + + private UntrustedWSServer server; + private WebSocketContainer client; + + @Before + public void initClient() + { + client = ContainerProvider.getWebSocketContainer(); + } + + @Before + public void startServer() throws Exception + { + server = new UntrustedWSServer(); + server.start(); + } + + @After + public void stopServer() throws Exception + { + server.stop(); + } + + @Test + public void testManyIds() throws Exception + { + server.registerWebSocket("/eventids", new EventIdServerCreator()); + + URI wsUri = server.getWsUri().resolve("/eventids"); + EventIdSocket clientSocket = new EventIdSocket(testname.getMethodName()); + Session clientSession = client.connectToServer(clientSocket, wsUri); + + final int from = 1000; + final int to = 2000; + + clientSession.getAsyncRemote().sendText("seq|" + from + "|" + to); + + // collect seen ids + List seen = new ArrayList<>(); + for (int i = from; i < to; i++) + { + // validate that ids don't repeat. + EventId receivedId = clientSocket.messageQueue.poll(5, TimeUnit.SECONDS); + Assert.assertFalse("Already saw ID: " + receivedId.eventId, seen.contains(receivedId.eventId)); + seen.add(receivedId.eventId); + } + + // validate that all expected ids have been seen (order is irrelevant here) + for (int expected = from; expected < to; expected++) + { + Assert.assertTrue("Has expected id:" + expected, seen.contains(expected)); + } + } +} diff --git a/jetty-websocket/websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/client/jsr356/Quotes.java b/jetty-websocket/websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/client/jsr356/Quotes.java new file mode 100644 index 00000000000..6503ec956a5 --- /dev/null +++ b/jetty-websocket/websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/client/jsr356/Quotes.java @@ -0,0 +1,48 @@ +// +// ======================================================================== +// 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.client.jsr356; + +import java.util.ArrayList; +import java.util.List; + +public class Quotes +{ + private String author; + private List quotes = new ArrayList<>(); + + public void addQuote(String quote) + { + quotes.add(quote); + } + + public String getAuthor() + { + return author; + } + + public List getQuotes() + { + return quotes; + } + + public void setAuthor(String author) + { + this.author = author; + } +} diff --git a/jetty-websocket/websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/client/jsr356/QuotesDecoder.java b/jetty-websocket/websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/client/jsr356/QuotesDecoder.java new file mode 100644 index 00000000000..9fe4d302e30 --- /dev/null +++ b/jetty-websocket/websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/client/jsr356/QuotesDecoder.java @@ -0,0 +1,73 @@ +// +// ======================================================================== +// 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.client.jsr356; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.Reader; + +import javax.websocket.DecodeException; +import javax.websocket.Decoder; +import javax.websocket.EndpointConfig; + +import org.eclipse.jetty.util.log.Log; +import org.eclipse.jetty.util.log.Logger; + +public class QuotesDecoder implements Decoder.TextStream +{ + private static final Logger LOG = Log.getLogger(QuotesDecoder.class); + + @Override + public void init(EndpointConfig config) + { + // TODO: verify init called + } + + @Override + public void destroy() + { + // TODO: verify destroy called + } + + @Override + public Quotes decode(Reader reader) throws DecodeException, IOException + { + Quotes quotes = new Quotes(); + try (BufferedReader buf = new BufferedReader(reader)) + { + LOG.debug("decode() begin"); + String line; + while ((line = buf.readLine()) != null) + { + LOG.debug("decode() line = {}", line); + switch (line.charAt(0)) + { + case 'a': + quotes.setAuthor(line.substring(2)); + break; + case 'q': + quotes.addQuote(line.substring(2)); + break; + } + } + LOG.debug("decode() complete"); + } + return quotes; + } +} diff --git a/jetty-websocket/websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/client/jsr356/QuotesDecoderTest.java b/jetty-websocket/websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/client/jsr356/QuotesDecoderTest.java new file mode 100644 index 00000000000..4f04043243d --- /dev/null +++ b/jetty-websocket/websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/client/jsr356/QuotesDecoderTest.java @@ -0,0 +1,215 @@ +// +// ======================================================================== +// 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.client.jsr356; + +import static org.hamcrest.Matchers.is; +import static org.junit.Assert.assertThat; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.net.URI; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingDeque; +import java.util.concurrent.TimeUnit; + +import javax.websocket.ClientEndpoint; +import javax.websocket.ContainerProvider; +import javax.websocket.OnMessage; +import javax.websocket.Session; +import javax.websocket.WebSocketContainer; + +import org.eclipse.jetty.toolchain.test.MavenTestingUtils; +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.WebSocketBehavior; +import org.eclipse.jetty.websocket.common.WebSocketFrame; +import org.eclipse.jetty.websocket.common.frames.ContinuationFrame; +import org.eclipse.jetty.websocket.common.frames.TextFrame; +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.tests.AbstractJsrTrackingEndpoint; +import org.eclipse.jetty.websocket.tests.UntrustedWSConnection; +import org.eclipse.jetty.websocket.tests.UntrustedWSEndpoint; +import org.eclipse.jetty.websocket.tests.UntrustedWSServer; +import org.eclipse.jetty.websocket.tests.UntrustedWSSession; +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TestName; + +public class QuotesDecoderTest +{ + @ClientEndpoint(decoders = QuotesDecoder.class, subprotocols = "quotes") + public static class QuotesSocket extends AbstractJsrTrackingEndpoint + { + public BlockingQueue messageQueue = new LinkedBlockingDeque<>(); + + public QuotesSocket(String id) + { + super(id); + } + + @SuppressWarnings("unused") + @OnMessage + public void onMessage(Quotes quote) + { + System.err.printf("QuotesSocket.onMessage(%s)%n",quote); + messageQueue.offer(quote); + } + } + + public static class QuoteServingCreator implements WebSocketCreator + { + @Override + public Object createWebSocket(ServletUpgradeRequest req, ServletUpgradeResponse resp) + { + QuoterServerEndpoint endpoint = new QuoterServerEndpoint(WebSocketBehavior.SERVER.name()); + resp.setAcceptedSubProtocol("quotes"); + return endpoint; + } + } + + public static class QuoterServerEndpoint extends UntrustedWSEndpoint + { + public QuoterServerEndpoint(String id) + { + super(id); + } + + @Override + public void onWebSocketText(String filename) + { + super.onWebSocketText(filename); + try + { + UntrustedWSSession untrustedWSSession = (UntrustedWSSession) session; + UntrustedWSConnection untrustedWSConnection = untrustedWSSession.getUntrustedConnection(); + writeQuotes(filename, untrustedWSConnection); + } + catch (Exception e) + { + e.printStackTrace(); + } + } + + public void writeQuotes(String filename, UntrustedWSConnection connection) throws Exception + { + // read file + File qfile = MavenTestingUtils.getTestResourceFile(filename); + List lines = new ArrayList<>(); + try (FileReader reader = new FileReader(qfile); BufferedReader buf = new BufferedReader(reader)) + { + String line; + while ((line = buf.readLine()) != null) + { + lines.add(line); + } + } + // write file out, each line on a separate frame, but as + // 1 whole message + for (int i = 0; i < lines.size(); i++) + { + WebSocketFrame frame; + if (i == 0) + { + frame = new TextFrame(); + } + else + { + frame = new ContinuationFrame(); + } + frame.setFin((i >= (lines.size() - 1))); + frame.setPayload(BufferUtil.toBuffer(lines.get(i) + "\n")); + connection.write(frame); + } + } + } + + private static final Logger LOG = Log.getLogger(QuotesDecoderTest.class); + + @Rule + public TestName testname = new TestName(); + + private UntrustedWSServer server; + private WebSocketContainer client; + + @Before + public void initClient() + { + client = ContainerProvider.getWebSocketContainer(); + } + + @Before + public void startServer() throws Exception + { + server = new UntrustedWSServer(); + server.start(); + } + + @After + public void stopServer() throws Exception + { + server.stop(); + } + + @Test + public void testSingleQuotes() throws Exception + { + server.registerWebSocket("/quoter", new QuoteServingCreator()); + + URI wsUri = server.getWsUri().resolve("/quoter"); + QuotesSocket clientSocket = new QuotesSocket(testname.getMethodName()); + Session clientSession = client.connectToServer(clientSocket, wsUri); + + clientSession.getAsyncRemote().sendText("quotes-ben.txt"); + + Quotes quotes = clientSocket.messageQueue.poll(5, TimeUnit.SECONDS); + + assertThat("Quotes Author", quotes.getAuthor(), is("Benjamin Franklin")); + assertThat("Quotes Count", quotes.getQuotes().size(), is(3)); + } + + @Test + public void testTwoQuotes() throws Exception + { + server.registerWebSocket("/quoter", new QuoteServingCreator()); + + URI wsUri = server.getWsUri().resolve("/quoter"); + QuotesSocket clientSocket = new QuotesSocket(testname.getMethodName()); + Session clientSession = client.connectToServer(clientSocket, wsUri); + + clientSession.getAsyncRemote().sendText("quotes-ben.txt"); + clientSession.getAsyncRemote().sendText("quotes-twain.txt"); + + Quotes quotes; + quotes = clientSocket.messageQueue.poll(5, TimeUnit.SECONDS); + assertThat("Quotes Author", quotes.getAuthor(), is("Benjamin Franklin")); + assertThat("Quotes Count", quotes.getQuotes().size(), is(3)); + + quotes = clientSocket.messageQueue.poll(5, TimeUnit.SECONDS); + assertThat("Quotes Author", quotes.getAuthor(), is("Mark Twain")); + assertThat("Quotes Count", quotes.getQuotes().size(), is(4)); + } +} diff --git a/jetty-websocket/websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/client/jsr356/QuotesEncoder.java b/jetty-websocket/websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/client/jsr356/QuotesEncoder.java new file mode 100644 index 00000000000..fabdbe3467e --- /dev/null +++ b/jetty-websocket/websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/client/jsr356/QuotesEncoder.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.client.jsr356; + +import javax.websocket.EncodeException; +import javax.websocket.Encoder; +import javax.websocket.EndpointConfig; + +public class QuotesEncoder implements Encoder.Text +{ + @Override + public void destroy() + { + // TODO: verify destroy called + } + + @Override + public String encode(Quotes q) throws EncodeException + { + StringBuilder buf = new StringBuilder(); + buf.append("Author: ").append(q.getAuthor()); + buf.append(System.lineSeparator()); + for (String quote : q.getQuotes()) + { + buf.append("Quote: ").append(quote); + buf.append(System.lineSeparator()); + } + return buf.toString(); + } + + @Override + public void init(EndpointConfig config) + { + // TODO: verify init called + } +} diff --git a/jetty-websocket/websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/client/jsr356/QuotesEncoderTest.java b/jetty-websocket/websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/client/jsr356/QuotesEncoderTest.java new file mode 100644 index 00000000000..8542eefb33a --- /dev/null +++ b/jetty-websocket/websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/client/jsr356/QuotesEncoderTest.java @@ -0,0 +1,197 @@ +// +// ======================================================================== +// 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.client.jsr356; + +import static org.hamcrest.Matchers.containsString; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.net.URI; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingDeque; +import java.util.concurrent.TimeUnit; + +import javax.websocket.ClientEndpoint; +import javax.websocket.ContainerProvider; +import javax.websocket.EncodeException; +import javax.websocket.OnMessage; +import javax.websocket.Session; +import javax.websocket.WebSocketContainer; + +import org.eclipse.jetty.toolchain.test.MavenTestingUtils; +import org.eclipse.jetty.websocket.tests.AbstractJsrTrackingEndpoint; +import org.eclipse.jetty.websocket.tests.UntrustedWSServer; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TestName; + +public class QuotesEncoderTest +{ + @ClientEndpoint(encoders = QuotesEncoder.class, subprotocols = "echo") + public static class QuotesSocket extends AbstractJsrTrackingEndpoint + { + public BlockingQueue messageQueue = new LinkedBlockingDeque<>(); + + public QuotesSocket(String id) + { + super(id); + } + + @SuppressWarnings("unused") + @OnMessage + public void onMessage(String message) + { + messageQueue.offer(message); + } + + public void write(Quotes quotes) throws IOException, EncodeException + { + if (LOG.isDebugEnabled()) + LOG.debug("Writing Quotes: {}", quotes); + this.session.getBasicRemote().sendObject(quotes); + } + } + + @Rule + public TestName testname = new TestName(); + + private UntrustedWSServer server; + private WebSocketContainer client; + + @Before + public void initClient() + { + client = ContainerProvider.getWebSocketContainer(); + } + + @Before + public void startServer() throws Exception + { + server = new UntrustedWSServer(); + server.start(); + } + + @After + public void stopServer() throws Exception + { + server.stop(); + } + + private void assertReceivedQuotes(String result, Quotes quotes) + { + Assert.assertThat("Quote Author", result, containsString("Author: " + quotes.getAuthor())); + for (String quote : quotes.getQuotes()) + { + Assert.assertThat("Quote", result, containsString("Quote: " + quote)); + } + } + + @SuppressWarnings("Duplicates") + private Quotes getQuotes(String filename) throws IOException + { + Quotes quotes = new Quotes(); + + // read file + File qfile = MavenTestingUtils.getTestResourceFile(filename); + try (FileReader reader = new FileReader(qfile); BufferedReader buf = new BufferedReader(reader)) + { + String line; + while ((line = buf.readLine()) != null) + { + switch (line.charAt(0)) + { + case 'a': + quotes.setAuthor(line.substring(2)); + break; + case 'q': + quotes.addQuote(line.substring(2)); + break; + } + } + } + + return quotes; + } + + private void close(Session session) throws IOException + { + if (session != null) + { + session.close(); + } + } + + @Test + public void testSingleQuotes() throws Exception + { + URI wsUri = server.getUntrustedWsUri(this.getClass(), testname); + QuotesSocket quoter = new QuotesSocket(testname.getMethodName()); + + Session session = null; + try + { + session = client.connectToServer(quoter, wsUri); + + Quotes ben = getQuotes("quotes-ben.txt"); + quoter.write(ben); + + String incomingMessage = quoter.messageQueue.poll(5, TimeUnit.SECONDS); + assertReceivedQuotes(incomingMessage, ben); + } + finally + { + close(session); + } + } + + @Test + public void testTwoQuotes() throws Exception + { + URI wsUri = server.getUntrustedWsUri(this.getClass(), testname); + QuotesSocket quoter = new QuotesSocket(testname.getMethodName()); + + Session session = null; + try + { + session = client.connectToServer(quoter, wsUri); + + Quotes ben = getQuotes("quotes-ben.txt"); + Quotes twain = getQuotes("quotes-twain.txt"); + quoter.write(ben); + quoter.write(twain); + + String incomingQuote; + + incomingQuote = quoter.messageQueue.poll(5, TimeUnit.SECONDS); + assertReceivedQuotes(incomingQuote, ben); + + incomingQuote = quoter.messageQueue.poll(5, TimeUnit.SECONDS); + assertReceivedQuotes(incomingQuote, twain); + } + finally + { + close(session); + } + } +} diff --git a/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/ConfiguratorTest.java b/jetty-websocket/websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/server/jsr356/ConfiguratorTest.java similarity index 57% rename from jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/ConfiguratorTest.java rename to jetty-websocket/websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/server/jsr356/ConfiguratorTest.java index 17d96492490..8549adcce14 100644 --- a/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/ConfiguratorTest.java +++ b/jetty-websocket/websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/server/jsr356/ConfiguratorTest.java @@ -16,10 +16,10 @@ // ======================================================================== // -package org.eclipse.jetty.websocket.jsr356.server; +package org.eclipse.jetty.websocket.tests.server.jsr356; import static org.hamcrest.Matchers.is; -import static org.hamcrest.Matchers.nullValue; +import static org.hamcrest.Matchers.notNullValue; import static org.junit.Assert.assertThat; import java.io.PrintWriter; @@ -35,6 +35,7 @@ import java.util.List; import java.util.Locale; import java.util.Map; import java.util.TimeZone; +import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; @@ -54,29 +55,34 @@ import javax.websocket.server.ServerEndpointConfig; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.ServerConnector; import org.eclipse.jetty.servlet.ServletContextHandler; -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.websocket.api.extensions.ExtensionConfig; import org.eclipse.jetty.websocket.api.util.QuoteUtil; -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.common.test.IBlockheadClient; +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.jsr356.server.JsrCreator; +import org.eclipse.jetty.websocket.jsr356.server.ServerContainer; import org.eclipse.jetty.websocket.jsr356.server.deploy.WebSocketServerContainerInitializer; +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 ConfiguratorTest { private static final Logger LOG = Log.getLogger(ConfiguratorTest.class); - + public static class EmptyConfigurator extends ServerEndpointConfig.Configurator { } - + @ServerEndpoint(value = "/empty", configurator = EmptyConfigurator.class) public static class EmptySocket { @@ -86,7 +92,7 @@ public class ConfiguratorTest return message; } } - + public static class NoExtensionsConfigurator extends ServerEndpointConfig.Configurator { @Override @@ -95,7 +101,7 @@ public class ConfiguratorTest return Collections.emptyList(); } } - + @ServerEndpoint(value = "/no-extensions", configurator = NoExtensionsConfigurator.class) public static class NoExtensionsSocket { @@ -115,7 +121,7 @@ public class ConfiguratorTest } } } - + public static class CaptureHeadersConfigurator extends ServerEndpointConfig.Configurator { @Override @@ -125,7 +131,7 @@ public class ConfiguratorTest sec.getUserProperties().put("request-headers", request.getHeaders()); } } - + @ServerEndpoint(value = "/capture-request-headers", configurator = CaptureHeadersConfigurator.class) public static class CaptureHeadersSocket { @@ -133,7 +139,7 @@ public class ConfiguratorTest public String getHeaders(Session session, String headerKey) { StringBuilder response = new StringBuilder(); - + response.append("Request Header [").append(headerKey).append("]: "); @SuppressWarnings("unchecked") Map> headers = (Map>) session.getUserProperties().get("request-headers"); @@ -153,11 +159,11 @@ public class ConfiguratorTest response.append(QuoteUtil.join(values, ",")); } } - + return response.toString(); } } - + public static class ProtocolsConfigurator extends ServerEndpointConfig.Configurator { public static AtomicReference seenProtocols = new AtomicReference<>(); @@ -176,7 +182,7 @@ public class ConfiguratorTest return super.getNegotiatedSubprotocol(supported, requested); } } - + @ServerEndpoint(value = "/protocols", configurator = ProtocolsConfigurator.class) public static class ProtocolsSocket { @@ -268,17 +274,17 @@ public class ConfiguratorTest appendPropValue(session, response, "found.remote"); return response.toString(); } - + private void appendPropValue(Session session, StringBuilder response, String key) { InetSocketAddress value = (InetSocketAddress) session.getUserProperties().get(key); - + response.append("[").append(key).append("] = "); response.append(toSafeAddr(value)); response.append(System.lineSeparator()); } } - + public static class SelectedProtocolConfigurator extends ServerEndpointConfig.Configurator { @Override @@ -291,11 +297,11 @@ public class ConfiguratorTest config.getUserProperties().put("selected-subprotocol", protocol); } } - + public static class GmtTimeDecoder implements Decoder.Text { private TimeZone TZ; - + @Override public Calendar decode(String s) throws DecodeException { @@ -316,39 +322,39 @@ public class ConfiguratorTest throw new DecodeException(s, "Unable to decode Time", e); } } - + @Override public void init(EndpointConfig config) { TZ = TimeZone.getTimeZone("GMT+0"); } - + @Override public void destroy() { } - + @Override public boolean willDecode(String s) { return true; } } - + @ServerEndpoint(value = "/timedecoder", - subprotocols = { "time", "gmt" }, + subprotocols = {"time", "gmt"}, configurator = SelectedProtocolConfigurator.class, decoders = {GmtTimeDecoder.class}) public static class TimeDecoderSocket { private TimeZone TZ = TimeZone.getTimeZone("GMT+0"); - + @OnMessage public String onMessage(Calendar cal) { return String.format("cal=%s", newDateFormat().format(cal.getTime())); } - + private SimpleDateFormat newDateFormat() { SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy.MM.dd G 'at' HH:mm:ss Z", Locale.ENGLISH); @@ -359,7 +365,7 @@ public class ConfiguratorTest private static Server server; private static URI baseServerUri; - + @BeforeClass public static void startServer() throws Exception { @@ -367,11 +373,11 @@ public class ConfiguratorTest ServerConnector connector = new ServerConnector(server); connector.setPort(0); server.addConnector(connector); - + ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS); context.setContextPath("/"); server.setHandler(context); - + ServerContainer container = WebSocketServerContainerInitializer.configureContext(context); container.addEndpoint(CaptureHeadersSocket.class); container.addEndpoint(EmptySocket.class); @@ -380,19 +386,13 @@ public class ConfiguratorTest container.addEndpoint(UniqueUserPropsSocket.class); container.addEndpoint(AddressSocket.class); container.addEndpoint(TimeDecoderSocket.class); - + server.start(); - String host = connector.getHost(); - if (host == null) - { - host = "localhost"; - } - int port = connector.getLocalPort(); - baseServerUri = new URI(String.format("ws://%s:%d/", host, port)); + baseServerUri = WSURI.toWebsocket(server.getURI()).resolve("/"); if (LOG.isDebugEnabled()) LOG.debug("Server started on {}", baseServerUri); } - + public static String toSafeAddr(InetSocketAddress addr) { if (addr == null) @@ -401,250 +401,273 @@ public class ConfiguratorTest } return String.format("%s:%d", addr.getAddress().getHostAddress(), addr.getPort()); } - + @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 testEmptyConfigurator() throws Exception { - URI uri = baseServerUri.resolve("/empty"); - - try (IBlockheadClient client = new XBlockheadClient(uri)) - { - client.addExtensions("identity"); - client.connect(); - client.sendStandardRequest(); - HttpResponse response = client.readResponseHeader(); - Assert.assertThat("response.extensions", response.getExtensionsHeader(), is("identity")); - } + URI wsUri = baseServerUri.resolve("/empty"); + + TrackingEndpoint clientSocket = new TrackingEndpoint(testname.getMethodName()); + ClientUpgradeRequest upgradeRequest = new ClientUpgradeRequest(); + upgradeRequest.addExtensions("identity"); + Future clientConnectFuture = client.connect(clientSocket, wsUri, upgradeRequest); + + org.eclipse.jetty.websocket.api.Session clientSession = clientConnectFuture.get(Defaults.CONNECT_TIMEOUT_MS, TimeUnit.MILLISECONDS); + List negotiatedExtensions = clientSession.getUpgradeResponse().getExtensions(); + assertThat("UpgradeResponse.extensions", negotiatedExtensions, notNullValue()); + assertThat("UpgradeResponse.extensions.size", negotiatedExtensions.size(), is(1)); + assertThat("UpgradeResponse.extensions[0]", negotiatedExtensions.get(0).toString(), is("identity")); + + clientSession.close(); } - + @Test public void testNoExtensionsConfigurator() throws Exception { - URI uri = baseServerUri.resolve("/no-extensions"); - - try (IBlockheadClient client = new XBlockheadClient(uri)) - { - client.addExtensions("identity"); - client.connect(); - client.sendStandardRequest(); - HttpResponse response = client.expectUpgradeResponse(); - assertThat("response.extensions", response.getExtensionsHeader(), nullValue()); - - client.write(new TextFrame().setPayload("NegoExts")); - EventQueue frames = client.readFrames(1, 1, TimeUnit.SECONDS); - WebSocketFrame frame = frames.poll(); - assertThat("Frame Response", frame.getPayloadAsUTF8(), is("negotiatedExtensions=[]")); - } + URI wsUri = baseServerUri.resolve("/no-extensions"); + + TrackingEndpoint clientSocket = new TrackingEndpoint(testname.getMethodName()); + ClientUpgradeRequest upgradeRequest = new ClientUpgradeRequest(); + upgradeRequest.addExtensions("identity"); + Future clientConnectFuture = client.connect(clientSocket, wsUri, upgradeRequest); + + org.eclipse.jetty.websocket.api.Session clientSession = clientConnectFuture.get(Defaults.CONNECT_TIMEOUT_MS, TimeUnit.MILLISECONDS); + clientSession.getRemote().sendString("NegoExts"); + + String incomingMessage = clientSocket.messageQueue.poll(5, TimeUnit.SECONDS); + assertThat("Incoming Message", incomingMessage, is("negotiatedExtensions=[]")); + + clientSession.close(); } - + @Test public void testCaptureRequestHeadersConfigurator() throws Exception { - URI uri = baseServerUri.resolve("/capture-request-headers"); - - try (IBlockheadClient client = new XBlockheadClient(uri)) - { - client.addHeader("X-Dummy: Bogus\r\n"); - client.connect(); - client.sendStandardRequest(); - client.expectUpgradeResponse(); - - client.write(new TextFrame().setPayload("X-Dummy")); - EventQueue frames = client.readFrames(1, 1, TimeUnit.SECONDS); - WebSocketFrame frame = frames.poll(); - Assert.assertThat("Frame Response", frame.getPayloadAsUTF8(), is("Request Header [X-Dummy]: \"Bogus\"")); - } + URI wsUri = baseServerUri.resolve("/capture-request-headers"); + + TrackingEndpoint clientSocket = new TrackingEndpoint(testname.getMethodName()); + ClientUpgradeRequest upgradeRequest = new ClientUpgradeRequest(); + upgradeRequest.setHeader("X-Dummy", "Bogus"); + Future clientConnectFuture = client.connect(clientSocket, wsUri, upgradeRequest); + + org.eclipse.jetty.websocket.api.Session clientSession = clientConnectFuture.get(Defaults.CONNECT_TIMEOUT_MS, TimeUnit.MILLISECONDS); + clientSession.getRemote().sendString("X-Dummy"); + + String incomingMessage = clientSocket.messageQueue.poll(5, TimeUnit.SECONDS); + assertThat("Incoming Message", incomingMessage, is("Request Header [X-Dummy]: \"Bogus\"")); + + clientSession.close(); } @Test public void testUniqueUserPropsConfigurator() throws Exception { - URI uri = baseServerUri.resolve("/unique-user-props"); - - // First request - try (IBlockheadClient client = new XBlockheadClient(uri)) - { - client.connect(); - client.sendStandardRequest(); - client.expectUpgradeResponse(); - - client.write(new TextFrame().setPayload("apple")); - EventQueue frames = client.readFrames(1, 1, TimeUnit.SECONDS); - WebSocketFrame frame = frames.poll(); - Assert.assertThat("Frame Response", frame.getPayloadAsUTF8(), is("Requested User Property: [apple] = \"fruit from tree\"")); - } + URI wsUri = baseServerUri.resolve("/unique-user-props"); + + // First Request + TrackingEndpoint clientSocket = new TrackingEndpoint(testname.getMethodName()); + ClientUpgradeRequest upgradeRequest = new ClientUpgradeRequest(); + Future clientConnectFuture = client.connect(clientSocket, wsUri, upgradeRequest); + + org.eclipse.jetty.websocket.api.Session clientSession = clientConnectFuture.get(Defaults.CONNECT_TIMEOUT_MS, TimeUnit.MILLISECONDS); + clientSession.getRemote().sendString("apple"); // first request has this UserProperty + + String incomingMessage = clientSocket.messageQueue.poll(5, TimeUnit.SECONDS); + assertThat("Incoming Message", incomingMessage, is("Requested User Property: [apple] = \"fruit from tree\"")); + + clientSession.close(); // Second request - try (IBlockheadClient client = new XBlockheadClient(uri)) - { - client.connect(); - client.sendStandardRequest(); - client.expectUpgradeResponse(); - - client.write(new TextFrame().setPayload("apple")); - client.write(new TextFrame().setPayload("blueberry")); - EventQueue frames = client.readFrames(2, 1, TimeUnit.SECONDS); - WebSocketFrame frame = frames.poll(); - // should have no value - Assert.assertThat("Frame Response", frame.getPayloadAsUTF8(), is("Requested User Property: [apple] = ")); - - frame = frames.poll(); - Assert.assertThat("Frame Response", frame.getPayloadAsUTF8(), is("Requested User Property: [blueberry] = \"fruit from bush\"")); - } + 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("apple"); // as this is second request, this should be null + clientSession.getRemote().sendString("blueberry"); // second request has this UserProperty + + incomingMessage = clientSocket.messageQueue.poll(5, TimeUnit.SECONDS); + assertThat("Incoming Message", incomingMessage, is("Requested User Property: [apple] = ")); + incomingMessage = clientSocket.messageQueue.poll(5, TimeUnit.SECONDS); + assertThat("Incoming Message", incomingMessage, is("Requested User Property: [blueberry] = \"fruit from bush\"")); + + clientSession.close(); } @Test public void testUserPropsAddress() throws Exception { - URI uri = baseServerUri.resolve("/addr"); - - // First request - try (IBlockheadClient client = new XBlockheadClient(uri)) - { - client.connect(); - client.sendStandardRequest(); - client.expectUpgradeResponse(); - - InetSocketAddress expectedLocal = client.getLocalSocketAddress(); - InetSocketAddress expectedRemote = client.getRemoteSocketAddress(); - - client.write(new TextFrame().setPayload("addr")); - EventQueue frames = client.readFrames(1, 1, TimeUnit.SECONDS); - WebSocketFrame frame = frames.poll(); - - StringWriter expected = new StringWriter(); - PrintWriter out = new PrintWriter(expected); - // local <-> remote are opposite on server (duh) - out.printf("[javax.websocket.endpoint.localAddress] = %s%n", toSafeAddr(expectedRemote)); - out.printf("[javax.websocket.endpoint.remoteAddress] = %s%n", toSafeAddr(expectedLocal)); - out.printf("[found.local] = %s%n", toSafeAddr(expectedRemote)); - out.printf("[found.remote] = %s%n", toSafeAddr(expectedLocal)); - - Assert.assertThat("Frame Response", frame.getPayloadAsUTF8(), is(expected.toString())); - } + URI wsUri = baseServerUri.resolve("/addr"); + + TrackingEndpoint clientSocket = new TrackingEndpoint(testname.getMethodName()); + ClientUpgradeRequest upgradeRequest = new ClientUpgradeRequest(); + Future clientConnectFuture = client.connect(clientSocket, wsUri, upgradeRequest); + + org.eclipse.jetty.websocket.api.Session clientSession = clientConnectFuture.get(Defaults.CONNECT_TIMEOUT_MS, TimeUnit.MILLISECONDS); + InetSocketAddress expectedLocal = clientSession.getLocalAddress(); + InetSocketAddress expectedRemote = clientSession.getRemoteAddress(); + + clientSession.getRemote().sendString("addr"); + + String incomingMessage = clientSocket.messageQueue.poll(5, TimeUnit.SECONDS); + + StringWriter expected = new StringWriter(); + PrintWriter out = new PrintWriter(expected); + // local <-> remote are opposite on server (duh) + out.printf("[javax.websocket.endpoint.localAddress] = %s%n", toSafeAddr(expectedRemote)); + out.printf("[javax.websocket.endpoint.remoteAddress] = %s%n", toSafeAddr(expectedLocal)); + out.printf("[found.local] = %s%n", toSafeAddr(expectedRemote)); + out.printf("[found.remote] = %s%n", toSafeAddr(expectedLocal)); + + assertThat("Frame Response", incomingMessage, is(expected.toString())); + + clientSession.close(); } /** * Test of Sec-WebSocket-Protocol, as seen in RFC-6455, 1 protocol + * * @throws Exception on test failure */ @Test public void testProtocol_Single() throws Exception { - URI uri = baseServerUri.resolve("/protocols"); + URI wsUri = baseServerUri.resolve("/protocols"); ProtocolsConfigurator.seenProtocols.set(null); - - try (IBlockheadClient client = new XBlockheadClient(uri)) - { - client.addHeader("Sec-WebSocket-Protocol: echo\r\n"); - client.connect(); - client.sendStandardRequest(); - client.expectUpgradeResponse(); - - client.write(new TextFrame().setPayload("getProtocols")); - EventQueue frames = client.readFrames(1, 1, TimeUnit.SECONDS); - WebSocketFrame frame = frames.poll(); - Assert.assertThat("Frame Response", frame.getPayloadAsUTF8(), is("Requested Protocols: [\"echo\"]")); - } + + TrackingEndpoint clientSocket = new TrackingEndpoint(testname.getMethodName()); + ClientUpgradeRequest upgradeRequest = new ClientUpgradeRequest(); + upgradeRequest.setSubProtocols("echo"); + Future clientConnectFuture = client.connect(clientSocket, wsUri, upgradeRequest); + + org.eclipse.jetty.websocket.api.Session clientSession = clientConnectFuture.get(Defaults.CONNECT_TIMEOUT_MS, TimeUnit.MILLISECONDS); + clientSession.getRemote().sendString("getProtocols"); + + String incomingMessage = clientSocket.messageQueue.poll(5, TimeUnit.SECONDS); + assertThat("Incoming message", incomingMessage, is("Requested Protocols: [\"echo\"]")); + + clientSession.close(); } /** * Test of Sec-WebSocket-Protocol, as seen in RFC-6455, 3 protocols + * * @throws Exception on test failure */ @Test public void testProtocol_Triple() throws Exception { - URI uri = baseServerUri.resolve("/protocols"); + URI wsUri = baseServerUri.resolve("/protocols"); ProtocolsConfigurator.seenProtocols.set(null); - - try (IBlockheadClient client = new XBlockheadClient(uri)) - { - client.addHeader("Sec-WebSocket-Protocol: echo, chat, status\r\n"); - client.connect(); - client.sendStandardRequest(); - client.expectUpgradeResponse(); - - client.write(new TextFrame().setPayload("getProtocols")); - EventQueue frames = client.readFrames(1, 1, TimeUnit.SECONDS); - WebSocketFrame frame = frames.poll(); - Assert.assertThat("Frame Response", frame.getPayloadAsUTF8(), is("Requested Protocols: [\"echo\",\"chat\",\"status\"]")); - } + + TrackingEndpoint clientSocket = new TrackingEndpoint(testname.getMethodName()); + ClientUpgradeRequest upgradeRequest = new ClientUpgradeRequest(); + upgradeRequest.setSubProtocols("echo", "chat", "status"); + Future clientConnectFuture = client.connect(clientSocket, wsUri, upgradeRequest); + + org.eclipse.jetty.websocket.api.Session clientSession = clientConnectFuture.get(Defaults.CONNECT_TIMEOUT_MS, TimeUnit.MILLISECONDS); + clientSession.getRemote().sendString("getProtocols"); + + String incomingMessage = clientSocket.messageQueue.poll(5, TimeUnit.SECONDS); + assertThat("Incoming message", incomingMessage, is("Requested Protocols: [\"echo\",\"chat\",\"status\"]")); + + clientSession.close(); } /** * Test of Sec-WebSocket-Protocol, using all lowercase header + * * @throws Exception on test failure */ @Test public void testProtocol_LowercaseHeader() throws Exception { - URI uri = baseServerUri.resolve("/protocols"); + URI wsUri = baseServerUri.resolve("/protocols"); ProtocolsConfigurator.seenProtocols.set(null); - - try (IBlockheadClient client = new XBlockheadClient(uri)) - { - client.addHeader("sec-websocket-protocol: echo, chat, status\r\n"); - client.connect(); - client.sendStandardRequest(); - client.expectUpgradeResponse(); - - client.write(new TextFrame().setPayload("getProtocols")); - EventQueue frames = client.readFrames(1, 1, TimeUnit.SECONDS); - WebSocketFrame frame = frames.poll(); - Assert.assertThat("Frame Response", frame.getPayloadAsUTF8(), is("Requested Protocols: [\"echo\",\"chat\",\"status\"]")); - } + + TrackingEndpoint clientSocket = new TrackingEndpoint(testname.getMethodName()); + ClientUpgradeRequest upgradeRequest = new ClientUpgradeRequest(); + upgradeRequest.setHeader("sec-websocket-protocol", "echo, chat, status"); + Future clientConnectFuture = client.connect(clientSocket, wsUri, upgradeRequest); + + org.eclipse.jetty.websocket.api.Session clientSession = clientConnectFuture.get(Defaults.CONNECT_TIMEOUT_MS, TimeUnit.MILLISECONDS); + clientSession.getRemote().sendString("getProtocols"); + + String incomingMessage = clientSocket.messageQueue.poll(5, TimeUnit.SECONDS); + assertThat("Incoming message", incomingMessage, is("Requested Protocols: [\"echo\",\"chat\",\"status\"]")); + + clientSession.close(); } /** * Test of Sec-WebSocket-Protocol, using non-spec case header + * * @throws Exception on test failure */ @Test public void testProtocol_AltHeaderCase() throws Exception { - URI uri = baseServerUri.resolve("/protocols"); + URI wsUri = baseServerUri.resolve("/protocols"); ProtocolsConfigurator.seenProtocols.set(null); - - try (IBlockheadClient client = new XBlockheadClient(uri)) - { - client.addHeader("Sec-Websocket-Protocol: echo, chat, status\r\n"); - client.connect(); - client.sendStandardRequest(); - client.expectUpgradeResponse(); - - client.write(new TextFrame().setPayload("getProtocols")); - EventQueue frames = client.readFrames(1, 1, TimeUnit.SECONDS); - WebSocketFrame frame = frames.poll(); - Assert.assertThat("Frame Response", frame.getPayloadAsUTF8(), is("Requested Protocols: [\"echo\",\"chat\",\"status\"]")); - } + + TrackingEndpoint clientSocket = new TrackingEndpoint(testname.getMethodName()); + ClientUpgradeRequest upgradeRequest = new ClientUpgradeRequest(); + // header name is not to spec (case wise) + upgradeRequest.setHeader("Sec-Websocket-Protocol", "echo, chat, status"); + Future clientConnectFuture = client.connect(clientSocket, wsUri, upgradeRequest); + + org.eclipse.jetty.websocket.api.Session clientSession = clientConnectFuture.get(Defaults.CONNECT_TIMEOUT_MS, TimeUnit.MILLISECONDS); + clientSession.getRemote().sendString("getProtocols"); + + String incomingMessage = clientSocket.messageQueue.poll(5, TimeUnit.SECONDS); + assertThat("Incoming message", incomingMessage, is("Requested Protocols: [\"echo\",\"chat\",\"status\"]")); + + clientSession.close(); } - + /** * Test of Sec-WebSocket-Protocol, using non-spec case header */ @Test public void testDecoderWithProtocol() throws Exception { - URI uri = baseServerUri.resolve("/timedecoder"); - - try (XBlockheadClient client = new XBlockheadClient(uri)) - { - client.addHeader("Sec-Websocket-Protocol: gmt\r\n"); - client.connect(); - client.sendStandardRequest(); - client.expectUpgradeResponse(); - - client.write(new TextFrame().setPayload("2016-06-20T14:27:44")); - EventQueue frames = client.readFrames(1, 1, TimeUnit.SECONDS); - WebSocketFrame frame = frames.poll(); - Assert.assertThat("Frame Response", frame.getPayloadAsUTF8(), is("cal=2016.06.20 AD at 14:27:44 +0000")); - } + URI wsUri = baseServerUri.resolve("/timedecoder"); + + TrackingEndpoint clientSocket = new TrackingEndpoint(testname.getMethodName()); + ClientUpgradeRequest upgradeRequest = new ClientUpgradeRequest(); + upgradeRequest.setSubProtocols("gmt"); + Future clientConnectFuture = client.connect(clientSocket, wsUri, upgradeRequest); + + org.eclipse.jetty.websocket.api.Session clientSession = clientConnectFuture.get(Defaults.CONNECT_TIMEOUT_MS, TimeUnit.MILLISECONDS); + clientSession.getRemote().sendString("2016-06-20T14:27:44"); + + String incomingMessage = clientSocket.messageQueue.poll(5, TimeUnit.SECONDS); + assertThat("Incoming message", incomingMessage, is("cal=2016.06.20 AD at 14:27:44 +0000")); + + clientSession.close(); } } diff --git a/jetty-websocket/websocket-tests/src/test/resources/jetty-logging.properties b/jetty-websocket/websocket-tests/src/test/resources/jetty-logging.properties index 893b7d808d2..a63fd274602 100644 --- a/jetty-websocket/websocket-tests/src/test/resources/jetty-logging.properties +++ b/jetty-websocket/websocket-tests/src/test/resources/jetty-logging.properties @@ -26,6 +26,10 @@ org.eclipse.jetty.LEVEL=WARN # org.eclipse.jetty.websocket.LEVEL=DEBUG # org.eclipse.jetty.websocket.LEVEL=INFO # org.eclipse.jetty.websocket.tests.LEVEL=DEBUG +# org.eclipse.jetty.websocket.tests.client.LEVEL=DEBUG +# org.eclipse.jetty.websocket.tests.client.jsr356.LEVEL=DEBUG +# org.eclipse.jetty.websocket.tests.server.LEVEL=DEBUG +# org.eclipse.jetty.websocket.tests.server.jsr356.LEVEL=DEBUG # org.eclipse.jetty.websocket.common.io.LEVEL=DEBUG # org.eclipse.jetty.websocket.server.ab.LEVEL=DEBUG # org.eclipse.jetty.websocket.common.WebSocketSession.LEVEL=DEBUG @@ -36,17 +40,5 @@ org.eclipse.jetty.LEVEL=WARN # org.eclipse.jetty.websocket.server.helper.LEVEL=DEBUG org.eclipse.jetty.websocket.common.CompletionCallback.LEVEL=ALL -# org.eclipse.jetty.websocket.client.io.ConnectPromise.LEVEL=DEBUG -# org.eclipse.jetty.websocket.common.WebSocketSession_OPEN.LEVEL=DEBUG -# org.eclipse.jetty.websocket.common.io.AbstractWebSocketConnection_OPEN.LEVEL=DEBUG - -### Show state changes on BrowserDebugTool -# -- LEAVE THIS AT DEBUG LEVEL -- -org.eclipse.jetty.websocket.server.browser.LEVEL=DEBUG - ### Disabling intentional error out of RFCSocket 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 diff --git a/jetty-websocket/websocket-tests/src/test/resources/quotes-ben.txt b/jetty-websocket/websocket-tests/src/test/resources/quotes-ben.txt new file mode 100644 index 00000000000..6b9ae8d02bd --- /dev/null +++ b/jetty-websocket/websocket-tests/src/test/resources/quotes-ben.txt @@ -0,0 +1,4 @@ +a|Benjamin Franklin +q|There never was a good war or a bad peace. +q|We must, indeed, all hang together, or assuredly we shall all hang separately. +q|Our new Constitution is now established, and has an appearance that promises permanency; but in this world nothing can be said to be certain, except death and taxes. \ No newline at end of file diff --git a/jetty-websocket/websocket-tests/src/test/resources/quotes-twain.txt b/jetty-websocket/websocket-tests/src/test/resources/quotes-twain.txt new file mode 100644 index 00000000000..4a0d6bc1288 --- /dev/null +++ b/jetty-websocket/websocket-tests/src/test/resources/quotes-twain.txt @@ -0,0 +1,5 @@ +a|Mark Twain +q|He is now fast rising from affluence to poverty. +q|A baby is an inestimable blessing and bother. +q|As I slowly grow wise I briskly grow cautious. +q|A circle is a round straight line with a hole in the middle. \ No newline at end of file