From 9a4b7807810d884a56a8022f48021a9def75e761 Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Thu, 2 Aug 2018 11:38:09 -0500 Subject: [PATCH] Issue #2767 - WebSocket Policy on JSR356 ClientContainer not represented correctly + Added Client unit tests for large messages + Re-enabled Server unit tests for large messages + Added more Server unit tests for large messages + In case of JSR356 Server with policy (and Behavior of SERVER) is controlling javax.websocker.server.ServerContainer and a user chooses to use that ServerContainer to connect to a remote websocket endpoint (using ServerContainer as a client), then the policy is delegated down to the Client Container with a different behavior (only). Signed-off-by: Joakim Erdfelt --- .../websocket/jsr356/ClientContainer.java | 12 +- .../jetty/websocket/jsr356/JsrSession.java | 8 + .../websocket/jsr356/JettyEchoSocket.java | 11 + .../websocket/jsr356/LargeMessageTest.java | 105 ++++++++++ .../jsr356/server/LargeAnnotatedTest.java | 87 ++++---- ...geClientContainerInitAsServerListener.java | 39 ++++ .../server/LargeClientContainerServlet.java | 136 ++++++++++++ .../LargeNestedClientContainerTest.java | 165 +++++++++++++++ .../LargeOnOpenSessionConfiguredTest.java | 99 +++++++++ ...rverContainerAsClientContainerServlet.java | 134 ++++++++++++ .../websocket/jsr356/server/WSServer.java | 46 +++-- .../echo/LargeEchoAnnotatedSocket.java | 46 +++++ ...-container-servlet-init-use-server-web.xml | 22 ++ .../large-client-container-servlet-web.xml | 18 ++ ...server-as-client-container-servlet-web.xml | 18 ++ .../jetty/websocket/api/WebSocketPolicy.java | 194 ++++++++++++++++-- .../websocket/client/WebSocketClient.java | 2 +- .../jetty/websocket/common/Parser.java | 1 - 18 files changed, 1053 insertions(+), 90 deletions(-) create mode 100644 jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/LargeMessageTest.java create mode 100644 jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/LargeClientContainerInitAsServerListener.java create mode 100644 jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/LargeClientContainerServlet.java create mode 100644 jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/LargeNestedClientContainerTest.java create mode 100644 jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/LargeOnOpenSessionConfiguredTest.java create mode 100644 jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/LargeServerContainerAsClientContainerServlet.java create mode 100644 jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/samples/echo/LargeEchoAnnotatedSocket.java create mode 100644 jetty-websocket/javax-websocket-server-impl/src/test/resources/large-client-container-servlet-init-use-server-web.xml create mode 100644 jetty-websocket/javax-websocket-server-impl/src/test/resources/large-client-container-servlet-web.xml create mode 100644 jetty-websocket/javax-websocket-server-impl/src/test/resources/large-server-as-client-container-servlet-web.xml diff --git a/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/ClientContainer.java b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/ClientContainer.java index c3de70d4024..9efe4361ea2 100644 --- a/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/ClientContainer.java +++ b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/ClientContainer.java @@ -406,7 +406,7 @@ public class ClientContainer extends ContainerLifeCycle implements WebSocketCont @Override public WebSocketPolicy getPolicy() { - return scopeDelegate.getPolicy(); + return client.getPolicy(); } @Override @@ -482,10 +482,11 @@ public class ClientContainer extends ContainerLifeCycle implements WebSocketCont @Override public void setDefaultMaxBinaryMessageBufferSize(int max) { - // overall message limit (used in non-streaming) - client.getPolicy().setMaxBinaryMessageSize(max); // incoming streaming buffer size client.setMaxBinaryMessageBufferSize(max); + + // bump overall message limit (used in non-streaming) + client.getPolicy().setMaxBinaryMessageSize(max); } @Override @@ -497,9 +498,10 @@ public class ClientContainer extends ContainerLifeCycle implements WebSocketCont @Override public void setDefaultMaxTextMessageBufferSize(int max) { - // overall message limit (used in non-streaming) - client.getPolicy().setMaxTextMessageSize(max); // incoming streaming buffer size client.setMaxTextMessageBufferSize(max); + + // bump overall message limit (used in non-streaming) + client.getPolicy().setMaxTextMessageSize(max); } } diff --git a/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/JsrSession.java b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/JsrSession.java index 2eadbd1bbb1..c678a0d304f 100644 --- a/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/JsrSession.java +++ b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/JsrSession.java @@ -328,7 +328,11 @@ public class JsrSession extends WebSocketSession implements javax.websocket.Sess @Override public void setMaxBinaryMessageBufferSize(int length) { + // incoming streaming buffer size getPolicy().setMaxBinaryMessageBufferSize(length); + + // bump overall message limit (used in non-streaming) + getPolicy().setMaxBinaryMessageSize(length); } @Override @@ -341,7 +345,11 @@ public class JsrSession extends WebSocketSession implements javax.websocket.Sess @Override public void setMaxTextMessageBufferSize(int length) { + // incoming streaming buffer size getPolicy().setMaxTextMessageBufferSize(length); + + // bump overall message limit (used in non-streaming) + getPolicy().setMaxTextMessageSize(length); } public void setPathParameters(Map pathParams) diff --git a/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/JettyEchoSocket.java b/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/JettyEchoSocket.java index 9bcbb38c86e..81bd2b7df35 100644 --- a/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/JettyEchoSocket.java +++ b/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/JettyEchoSocket.java @@ -26,6 +26,7 @@ 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.StatusCode; import org.eclipse.jetty.websocket.api.WebSocketAdapter; /** @@ -78,4 +79,14 @@ public class JettyEchoSocket extends WebSocketAdapter throw new RuntimeIOException(x); } } + + @Override + public void onWebSocketClose(int statusCode, String reason) + { + super.onWebSocketClose(statusCode, reason); + if (statusCode != StatusCode.NORMAL) + { + LOG.warn("Closed {} {}", statusCode, reason); + } + } } diff --git a/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/LargeMessageTest.java b/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/LargeMessageTest.java new file mode 100644 index 00000000000..e1e8728e5d1 --- /dev/null +++ b/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/LargeMessageTest.java @@ -0,0 +1,105 @@ +// +// ======================================================================== +// Copyright (c) 1995-2018 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 java.nio.charset.StandardCharsets.UTF_8; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.instanceOf; +import static org.hamcrest.Matchers.is; + +import java.net.URI; +import java.util.Arrays; +import java.util.concurrent.TimeUnit; + +import javax.websocket.ContainerProvider; +import javax.websocket.Session; +import javax.websocket.WebSocketContainer; + +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.ServerConnector; +import org.eclipse.jetty.server.handler.ContextHandler; +import org.eclipse.jetty.websocket.api.WebSocketBehavior; +import org.eclipse.jetty.websocket.api.WebSocketPolicy; +import org.eclipse.jetty.websocket.api.util.WSURI; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +public class LargeMessageTest +{ + private static final int LARGER_THAN_DEFAULT_SIZE; + private Server server; + + static + { + WebSocketPolicy defaultPolicy = new WebSocketPolicy(WebSocketBehavior.CLIENT); + LARGER_THAN_DEFAULT_SIZE = defaultPolicy.getMaxTextMessageSize() * 3; + } + + @Before + public void startServer() throws Exception + { + server = new Server(); + ServerConnector connector = new ServerConnector(server); + connector.setPort(0); + server.addConnector(connector); + + // This handler is expected to handle echoing of 2MB messages (max) + EchoHandler echoHandler = new EchoHandler(); + + ContextHandler context = new ContextHandler(); + context.setContextPath("/"); + context.setHandler(echoHandler); + server.setHandler(context); + + server.start(); + } + + @After + public void stopServer() throws Exception + { + server.stop(); + } + + @SuppressWarnings("Duplicates") + @Test + public void testLargeEcho_AsEndpointInstance() throws Exception + { + WebSocketContainer container = ContainerProvider.getWebSocketContainer(); + server.addBean(container); // allow to shutdown with server + + container.setDefaultMaxTextMessageBufferSize(LARGER_THAN_DEFAULT_SIZE); + + EndpointEchoClient echoer = new EndpointEchoClient(); + Assert.assertThat(echoer,instanceOf(javax.websocket.Endpoint.class)); + + URI wsUri = WSURI.toWebsocket(server.getURI()).resolve("/"); + + // Issue connect using instance of class that extends Endpoint + Session session = container.connectToServer(echoer,wsUri); + byte buf[] = new byte[LARGER_THAN_DEFAULT_SIZE]; + Arrays.fill(buf, (byte)'x'); + String message = new String(buf, UTF_8); + session.getBasicRemote().sendText(message); + + String echoed = echoer.textCapture.messages.poll(1, TimeUnit.SECONDS); + assertThat("Echoed", echoed, is(message)); + } +} diff --git a/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/LargeAnnotatedTest.java b/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/LargeAnnotatedTest.java index f9ea5de8dfb..47e9e860368 100644 --- a/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/LargeAnnotatedTest.java +++ b/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/LargeAnnotatedTest.java @@ -20,76 +20,79 @@ package org.eclipse.jetty.websocket.jsr356.server; import java.net.URI; import java.nio.charset.StandardCharsets; +import java.nio.file.Path; import java.util.Arrays; import java.util.concurrent.Future; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; -import org.eclipse.jetty.io.ByteBufferPool; -import org.eclipse.jetty.io.MappedByteBufferPool; -import org.eclipse.jetty.toolchain.test.TestingDir; +import org.eclipse.jetty.toolchain.test.MavenTestingUtils; import org.eclipse.jetty.webapp.WebAppContext; import org.eclipse.jetty.websocket.api.Session; import org.eclipse.jetty.websocket.client.WebSocketClient; import org.eclipse.jetty.websocket.common.test.Timeouts; -import org.eclipse.jetty.websocket.jsr356.server.samples.echo.LargeEchoConfiguredSocket; +import org.eclipse.jetty.websocket.jsr356.server.samples.echo.LargeEchoAnnotatedSocket; +import org.junit.AfterClass; import org.junit.Assert; -import org.junit.Ignore; -import org.junit.Rule; +import org.junit.BeforeClass; import org.junit.Test; /** - * Test Echo of Large messages, targeting the {@link javax.websocket.Session#setMaxTextMessageBufferSize(int)} functionality + * Test Echo of Large messages, targeting the {@code @OnMessage(maxMessage=###)} functionality */ -@Ignore public class LargeAnnotatedTest { - @Rule - public TestingDir testdir = new TestingDir(); + private static WSServer server; - public ByteBufferPool bufferPool = new MappedByteBufferPool(); + @BeforeClass + public static void startServer() throws Exception + { + Path testDir = MavenTestingUtils.getTargetTestingPath(LargeOnOpenSessionConfiguredTest.class.getSimpleName()); + server = new WSServer(testDir,"app"); + server.createWebInf(); + server.copyEndpoint(LargeEchoAnnotatedSocket.class); + + server.start(); + } + + @AfterClass + public static void stopServer() + { + server.stop(); + } + + @SuppressWarnings("Duplicates") @Test public void testEcho() throws Exception { - WSServer wsb = new WSServer(testdir,"app"); - wsb.createWebInf(); - wsb.copyEndpoint(LargeEchoConfiguredSocket.class); + URI uri = server.getServerBaseURI(); + WebAppContext webapp = server.createWebAppContext(); + server.deployWebapp(webapp); + // wsb.dump(); + + WebSocketClient client = new WebSocketClient(); try { - wsb.start(); - URI uri = wsb.getServerBaseURI(); + client.getPolicy().setMaxTextMessageSize(128*1024); + client.start(); + JettyEchoSocket clientEcho = new JettyEchoSocket(); + Future foo = client.connect(clientEcho,uri.resolve("echo/large")); - WebAppContext webapp = wsb.createWebAppContext(); - wsb.deployWebapp(webapp); - // wsb.dump(); - - WebSocketClient client = new WebSocketClient(bufferPool); - try - { - client.getPolicy().setMaxTextMessageSize(128*1024); - client.start(); - JettyEchoSocket clientEcho = new JettyEchoSocket(); - Future foo = client.connect(clientEcho,uri.resolve("echo/large")); - // wait for connect - foo.get(1,TimeUnit.SECONDS); - // The message size should be bigger than default, but smaller than the limit that LargeEchoSocket specifies - byte txt[] = new byte[100 * 1024]; - Arrays.fill(txt,(byte)'o'); - String msg = new String(txt,StandardCharsets.UTF_8); - clientEcho.sendMessage(msg); - LinkedBlockingQueue msgs = clientEcho.incomingMessages; - Assert.assertEquals("Expected message",msg,msgs.poll(Timeouts.POLL_EVENT, Timeouts.POLL_EVENT_UNIT)); - } - finally - { - client.stop(); - } + // wait for connect + foo.get(1,TimeUnit.SECONDS); + // The message size should be bigger than default, but smaller than the limit that LargeEchoSocket specifies + byte txt[] = new byte[100 * 1024]; + Arrays.fill(txt,(byte)'o'); + String msg = new String(txt,StandardCharsets.UTF_8); + clientEcho.sendMessage(msg); + LinkedBlockingQueue msgs = clientEcho.incomingMessages; + Assert.assertEquals("Expected message",msg,msgs.poll(Timeouts.POLL_EVENT, Timeouts.POLL_EVENT_UNIT)); } finally { - wsb.stop(); + client.stop(); } } } diff --git a/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/LargeClientContainerInitAsServerListener.java b/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/LargeClientContainerInitAsServerListener.java new file mode 100644 index 00000000000..8258a51ef93 --- /dev/null +++ b/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/LargeClientContainerInitAsServerListener.java @@ -0,0 +1,39 @@ +// +// ======================================================================== +// Copyright (c) 1995-2018 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.server; + +import javax.servlet.ServletContextEvent; +import javax.servlet.ServletContextListener; + +import org.eclipse.jetty.websocket.jsr356.JettyClientContainerProvider; + +public class LargeClientContainerInitAsServerListener implements ServletContextListener +{ + @Override + public void contextInitialized(ServletContextEvent sce) + { + JettyClientContainerProvider.useServerContainer(true); + } + + @Override + public void contextDestroyed(ServletContextEvent sce) + { + /* ignore */ + } +} diff --git a/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/LargeClientContainerServlet.java b/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/LargeClientContainerServlet.java new file mode 100644 index 00000000000..12298ac33db --- /dev/null +++ b/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/LargeClientContainerServlet.java @@ -0,0 +1,136 @@ +// +// ======================================================================== +// Copyright (c) 1995-2018 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.server; + +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; + +import java.io.IOException; +import java.net.URI; +import java.util.Arrays; +import java.util.concurrent.TimeUnit; + +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.websocket.ClientEndpoint; +import javax.websocket.ContainerProvider; +import javax.websocket.DeploymentException; +import javax.websocket.OnMessage; +import javax.websocket.OnOpen; +import javax.websocket.Session; +import javax.websocket.WebSocketContainer; + +import org.eclipse.jetty.util.BlockingArrayQueue; +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.WebSocketBehavior; +import org.eclipse.jetty.websocket.api.WebSocketPolicy; + +public class LargeClientContainerServlet extends HttpServlet +{ + private static final Logger LOG = Log.getLogger(LargeClientContainerServlet.class); + private static final int LARGER_THAN_DEFAULT_SIZE; + private WebSocketContainer clientContainer; + + static + { + WebSocketPolicy defaultPolicy = new WebSocketPolicy(WebSocketBehavior.CLIENT); + LARGER_THAN_DEFAULT_SIZE = defaultPolicy.getMaxTextMessageSize() * 3; + } + + private synchronized WebSocketContainer getClientContainer() + { + if(clientContainer == null) + { + clientContainer = ContainerProvider.getWebSocketContainer(); + clientContainer.setDefaultMaxTextMessageBufferSize(LARGER_THAN_DEFAULT_SIZE); + } + + return clientContainer; + } + + @SuppressWarnings("Duplicates") + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException + { + int size = LARGER_THAN_DEFAULT_SIZE; + + String sizeParam = req.getParameter("size"); + if(StringUtil.isNotBlank(sizeParam)) + { + size = Integer.parseInt(sizeParam); + } + + byte buf[] = new byte[size]; + Arrays.fill(buf, (byte) 'x'); + + String destUrl = req.getParameter("destUrl"); + if(StringUtil.isBlank(destUrl)) + { + resp.sendError(HttpServletResponse.SC_EXPECTATION_FAILED, "Missing destUrl"); + return; + } + + WebSocketContainer client = getClientContainer(); + URI wsUri = URI.create(destUrl); + + try + { + Session session = client.connectToServer(EchoClientSocket.class, wsUri); + EchoClientSocket clientSocket = (EchoClientSocket) session.getUserProperties().get("endpoint"); + String message = new String(buf, UTF_8); + session.getBasicRemote().sendText(message); + String echoed = clientSocket.messages.poll(1, TimeUnit.SECONDS); + assertThat("Echoed", echoed, is(message)); + resp.setStatus(HttpServletResponse.SC_OK); + resp.getWriter().println("Success"); + } + catch (DeploymentException e) + { + LOG.warn("Unable to deploy client socket", e); + resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Deployment error"); + } + catch (InterruptedException e) + { + LOG.warn("Unable to find echoed message", e); + resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Echoed message missing?"); + } + } + + @ClientEndpoint + public static class EchoClientSocket + { + public BlockingArrayQueue messages = new BlockingArrayQueue<>(); + + @OnOpen + public void onOpen(Session session) + { + session.getUserProperties().put("endpoint", this); + } + + @OnMessage + public void onMessage(String msg) + { + messages.offer(msg); + } + } +} diff --git a/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/LargeNestedClientContainerTest.java b/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/LargeNestedClientContainerTest.java new file mode 100644 index 00000000000..db98e2e9d52 --- /dev/null +++ b/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/LargeNestedClientContainerTest.java @@ -0,0 +1,165 @@ +// +// ======================================================================== +// Copyright (c) 1995-2018 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.server; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; + +import java.net.URI; +import java.net.URLEncoder; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; + +import org.eclipse.jetty.client.HttpClient; +import org.eclipse.jetty.client.api.ContentResponse; +import org.eclipse.jetty.toolchain.test.MavenTestingUtils; +import org.eclipse.jetty.webapp.WebAppContext; +import org.eclipse.jetty.websocket.api.WebSocketBehavior; +import org.eclipse.jetty.websocket.api.WebSocketPolicy; +import org.eclipse.jetty.websocket.jsr356.server.samples.echo.LargeEchoAnnotatedSocket; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +/** + * Test send of large messages from within a Server using a ClientContainer + */ +@RunWith(Parameterized.class) +public class LargeNestedClientContainerTest +{ + public static abstract class WSServerConfig + { + private final String description; + + public WSServerConfig(String description) + { + this.description = description; + } + + public abstract void configure(WSServer server) throws Exception; + + @Override + public String toString() + { + return this.description; + } + } + + @Parameterized.Parameters(name = "[{index}] {0}") + public static List usecases() + { + List scenarios = new ArrayList<>(); + + scenarios.add(new WSServerConfig[]{ + new WSServerConfig("Servlet using ContainerProvider.getWebSocketContainer() (default)") + { + @Override + public void configure(WSServer server) throws Exception + { + server.copyWebInf("large-client-container-servlet-web.xml"); + server.copyClass(LargeClientContainerServlet.class); + server.copyEndpoint(LargeEchoAnnotatedSocket.class); + } + } + }); + + scenarios.add(new WSServerConfig[]{ + new WSServerConfig("Servlet using ContainerProvider.getWebSocketContainer() (init / server-container)") + { + @Override + public void configure(WSServer server) throws Exception + { + server.copyWebInf("large-client-container-servlet-init-use-server-web.xml"); + server.copyClass(LargeClientContainerInitAsServerListener.class); + server.copyClass(LargeClientContainerServlet.class); + server.copyEndpoint(LargeEchoAnnotatedSocket.class); + } + } + }); + + scenarios.add(new WSServerConfig[]{ + new WSServerConfig("Servlet using ServerContainer as ClientContainer") + { + @Override + public void configure(WSServer server) throws Exception + { + server.copyWebInf("large-client-container-servlet-web.xml"); + server.copyClass(LargeServerContainerAsClientContainerServlet.class); + server.copyEndpoint(LargeEchoAnnotatedSocket.class); + } + } + }); + + return scenarios; + } + + private static final AtomicInteger appDirIdx = new AtomicInteger(0); + + @Parameterized.Parameter + public WSServerConfig serverConfig; + + @Test + public void testLargeEcho() throws Exception + { + Path testDir = MavenTestingUtils.getTargetTestingPath(LargeNestedClientContainerTest.class.getSimpleName() + "-" + appDirIdx.getAndIncrement()); + + WSServer server = new WSServer(testDir, "app"); + server.createWebInf(); + + serverConfig.configure(server); + + try + { + server.start(); + + WebAppContext webapp = server.createWebAppContext(); + server.deployWebapp(webapp); + + // server.dump(); + + HttpClient client = new HttpClient(); + try + { + client.start(); + + URI destUri = server.getServerBaseURI().resolve("/app/echo/large"); + String destUrl = URLEncoder.encode(destUri.toASCIIString(), "utf-8"); + + WebSocketPolicy defaultPolicy = new WebSocketPolicy(WebSocketBehavior.CLIENT); + int defaultTextSize = defaultPolicy.getMaxTextMessageSize(); + + URI uri = server.getServer().getURI().resolve("/app/echo/servlet?size=" + (defaultTextSize * 2) + "&destUrl=" + destUrl); + + ContentResponse response = client.GET(uri); + + assertThat("Response.status", response.getStatus(), is(200)); + } + finally + { + client.stop(); + } + } + finally + { + server.stop(); + } + } +} diff --git a/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/LargeOnOpenSessionConfiguredTest.java b/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/LargeOnOpenSessionConfiguredTest.java new file mode 100644 index 00000000000..254a57b1772 --- /dev/null +++ b/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/LargeOnOpenSessionConfiguredTest.java @@ -0,0 +1,99 @@ +// +// ======================================================================== +// Copyright (c) 1995-2018 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.server; + +import java.net.URI; +import java.nio.charset.StandardCharsets; +import java.nio.file.Path; +import java.util.Arrays; +import java.util.concurrent.Future; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.TimeUnit; + +import org.eclipse.jetty.toolchain.test.MavenTestingUtils; +import org.eclipse.jetty.webapp.WebAppContext; +import org.eclipse.jetty.websocket.api.Session; +import org.eclipse.jetty.websocket.client.WebSocketClient; +import org.eclipse.jetty.websocket.common.test.Timeouts; +import org.eclipse.jetty.websocket.jsr356.server.samples.echo.LargeEchoConfiguredSocket; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +/** + * Test Echo of Large messages, targeting the use of {@link javax.websocket.Session#setMaxTextMessageBufferSize(int)} + * from within the {@code @OnOpen} method call functionality. + */ +public class LargeOnOpenSessionConfiguredTest +{ + private WSServer server; + + @Before + public void startServer() throws Exception + { + Path testDir = MavenTestingUtils.getTargetTestingPath(LargeOnOpenSessionConfiguredTest.class.getSimpleName()); + + server = new WSServer(testDir,"app"); + server.createWebInf(); + server.copyEndpoint(LargeEchoConfiguredSocket.class); + + server.start(); + } + + @After + public void stopServer() + { + server.stop(); + } + + @SuppressWarnings("Duplicates") + @Test + public void testEcho() throws Exception + { + URI uri = server.getServerBaseURI(); + + WebAppContext webapp = server.createWebAppContext(); + server.deployWebapp(webapp); + // wsb.dump(); + + WebSocketClient client = new WebSocketClient(); + try + { + client.getPolicy().setMaxTextMessageSize(128 * 1024); + client.start(); + JettyEchoSocket clientEcho = new JettyEchoSocket(); + Future foo = client.connect(clientEcho,uri.resolve("echo/large")); + + // wait for connect + foo.get(1,TimeUnit.SECONDS); + // The message size should be bigger than default, but smaller than the limit that LargeEchoSocket specifies + byte txt[] = new byte[100 * 1024]; + Arrays.fill(txt,(byte)'o'); + String msg = new String(txt,StandardCharsets.UTF_8); + clientEcho.sendMessage(msg); + LinkedBlockingQueue msgs = clientEcho.incomingMessages; + Assert.assertEquals("Expected message",msg,msgs.poll(Timeouts.POLL_EVENT, Timeouts.POLL_EVENT_UNIT)); + } + finally + { + client.stop(); + } + } +} diff --git a/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/LargeServerContainerAsClientContainerServlet.java b/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/LargeServerContainerAsClientContainerServlet.java new file mode 100644 index 00000000000..aa64330096c --- /dev/null +++ b/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/LargeServerContainerAsClientContainerServlet.java @@ -0,0 +1,134 @@ +// +// ======================================================================== +// Copyright (c) 1995-2018 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.server; + +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; + +import java.io.IOException; +import java.net.URI; +import java.util.Arrays; +import java.util.concurrent.TimeUnit; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.websocket.ClientEndpoint; +import javax.websocket.DeploymentException; +import javax.websocket.OnMessage; +import javax.websocket.OnOpen; +import javax.websocket.Session; +import javax.websocket.WebSocketContainer; +import javax.websocket.server.ServerContainer; + +import org.eclipse.jetty.util.BlockingArrayQueue; +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.WebSocketBehavior; +import org.eclipse.jetty.websocket.api.WebSocketPolicy; + +public class LargeServerContainerAsClientContainerServlet extends HttpServlet +{ + private static final Logger LOG = Log.getLogger(LargeServerContainerAsClientContainerServlet.class); + private static final int LARGER_THAN_DEFAULT_SIZE; + private WebSocketContainer clientContainer; + + static + { + WebSocketPolicy defaultPolicy = new WebSocketPolicy(WebSocketBehavior.CLIENT); + LARGER_THAN_DEFAULT_SIZE = defaultPolicy.getMaxTextMessageSize() * 3; + } + + @Override + public void init() throws ServletException + { + super.init(); + + ServerContainer serverContainer = (ServerContainer) getServletContext().getAttribute(ServerContainer.class.getName()); + clientContainer = serverContainer; + } + + @SuppressWarnings("Duplicates") + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException + { + int size = LARGER_THAN_DEFAULT_SIZE; + + String sizeParam = req.getParameter("size"); + if(StringUtil.isNotBlank(sizeParam)) + { + size = Integer.parseInt(sizeParam); + } + + byte buf[] = new byte[size]; + Arrays.fill(buf, (byte) 'x'); + + String destUrl = req.getParameter("destUrl"); + if(StringUtil.isBlank(destUrl)) + { + resp.sendError(HttpServletResponse.SC_EXPECTATION_FAILED, "Missing destUrl"); + return; + } + + URI wsUri = URI.create(destUrl); + + try + { + Session session = clientContainer.connectToServer(EchoClientSocket.class, wsUri); + EchoClientSocket clientSocket = (EchoClientSocket) session.getUserProperties().get("endpoint"); + String message = new String(buf, UTF_8); + session.getBasicRemote().sendText(message); + String echoed = clientSocket.messages.poll(1, TimeUnit.SECONDS); + assertThat("Echoed", echoed, is(message)); + resp.setStatus(HttpServletResponse.SC_OK); + resp.getWriter().println("Success"); + } + catch (DeploymentException e) + { + LOG.warn("Unable to deploy client socket", e); + resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Deployment error"); + } + catch (InterruptedException e) + { + LOG.warn("Unable to find echoed message", e); + resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Echoed message missing?"); + } + } + + @ClientEndpoint + public static class EchoClientSocket + { + public BlockingArrayQueue messages = new BlockingArrayQueue<>(); + + @OnOpen + public void onOpen(Session session) + { + session.getUserProperties().put("endpoint", this); + } + + @OnMessage + public void onMessage(String msg) + { + messages.offer(msg); + } + } +} diff --git a/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/WSServer.java b/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/WSServer.java index 4547edcafb3..760e292974d 100644 --- a/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/WSServer.java +++ b/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/WSServer.java @@ -22,9 +22,9 @@ import static org.hamcrest.Matchers.notNullValue; import java.io.File; import java.io.IOException; -import java.net.MalformedURLException; import java.net.URI; import java.net.URL; +import java.nio.file.Path; import org.eclipse.jetty.annotations.AnnotationConfiguration; import org.eclipse.jetty.plus.webapp.EnvConfiguration; @@ -36,11 +36,10 @@ 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.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.util.resource.PathResource; import org.eclipse.jetty.webapp.Configuration; import org.eclipse.jetty.webapp.FragmentConfiguration; import org.eclipse.jetty.webapp.MetaInfConfiguration; @@ -57,22 +56,27 @@ import org.junit.Assert; public class WSServer { private static final Logger LOG = Log.getLogger(WSServer.class); - private final File contextDir; + private final Path contextDir; private final String contextPath; private Server server; private URI serverUri; private ContextHandlerCollection contexts; - private File webinf; - private File classesDir; + private Path webinf; + private Path classesDir; public WSServer(TestingDir testdir, String contextName) { - this(testdir.getPath().toFile(),contextName); + this(testdir.getPath(),contextName); } public WSServer(File testdir, String contextName) { - this.contextDir = new File(testdir,contextName); + this(testdir.toPath(), contextName); + } + + public WSServer(Path testdir, String contextName) + { + this.contextDir = testdir.resolve(contextName); this.contextPath = "/" + contextName; FS.ensureEmpty(contextDir); } @@ -83,10 +87,10 @@ public class WSServer String endpointPath = clazz.getName().replace('.','/') + ".class"; URL classUrl = cl.getResource(endpointPath); Assert.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); + Path destFile = classesDir.resolve(endpointPath); + FS.ensureDirExists(destFile.getParent()); + Path srcFile = new File(classUrl.toURI()).toPath(); + IO.copy(srcFile.toFile(),destFile.toFile()); } public void copyEndpoint(Class endpointClass) throws Exception @@ -96,23 +100,22 @@ public class WSServer public void copyWebInf(String testResourceName) throws IOException { - webinf = new File(contextDir,"WEB-INF"); + webinf = contextDir.resolve("WEB-INF"); FS.ensureDirExists(webinf); - classesDir = new File(webinf,"classes"); + classesDir = webinf.resolve("classes"); FS.ensureDirExists(classesDir); - File webxml = new File(webinf,"web.xml"); - File testWebXml = MavenTestingUtils.getTestResourceFile(testResourceName); - IO.copy(testWebXml,webxml); + Path webxml = webinf.resolve("web.xml"); + Path testWebXml = MavenTestingUtils.getTestResourcePath(testResourceName); + IO.copy(testWebXml.toFile(),webxml.toFile()); } - public WebAppContext createWebAppContext() throws MalformedURLException, IOException + public WebAppContext createWebAppContext() { WebAppContext context = new WebAppContext(); context.setContextPath(this.contextPath); - context.setBaseResource(Resource.newResource(this.contextDir)); + context.setBaseResource(new PathResource(this.contextDir)); context.setAttribute("org.eclipse.jetty.websocket.jsr356",Boolean.TRUE); - // @formatter:off context.setConfigurations(new Configuration[] { new AnnotationConfiguration(), new WebXmlConfiguration(), @@ -121,7 +124,6 @@ public class WSServer new MetaInfConfiguration(), new FragmentConfiguration(), new EnvConfiguration()}); - // @formatter:on return context; } @@ -159,7 +161,7 @@ public class WSServer public File getWebAppDir() { - return this.contextDir; + return this.contextDir.toFile(); } public void start() throws Exception diff --git a/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/samples/echo/LargeEchoAnnotatedSocket.java b/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/samples/echo/LargeEchoAnnotatedSocket.java new file mode 100644 index 00000000000..1c1b9422518 --- /dev/null +++ b/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/samples/echo/LargeEchoAnnotatedSocket.java @@ -0,0 +1,46 @@ +// +// ======================================================================== +// Copyright (c) 1995-2018 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.server.samples.echo; + +import javax.websocket.OnMessage; +import javax.websocket.OnOpen; +import javax.websocket.Session; +import javax.websocket.server.ServerEndpoint; + +/** + * Annotated echo socket + */ +@ServerEndpoint(value = "/echo/large") +public class LargeEchoAnnotatedSocket +{ + private Session session; + + @OnOpen + public void open(Session session) + { + this.session = session; + } + + @OnMessage(maxMessageSize = 128 * 1024) + public void echo(String msg) + { + // reply with echo + session.getAsyncRemote().sendText(msg); + } +} diff --git a/jetty-websocket/javax-websocket-server-impl/src/test/resources/large-client-container-servlet-init-use-server-web.xml b/jetty-websocket/javax-websocket-server-impl/src/test/resources/large-client-container-servlet-init-use-server-web.xml new file mode 100644 index 00000000000..534d945f1e3 --- /dev/null +++ b/jetty-websocket/javax-websocket-server-impl/src/test/resources/large-client-container-servlet-init-use-server-web.xml @@ -0,0 +1,22 @@ + + + + + org.eclipse.jetty.websocket.jsr356.server.LargeClientContainerInitAsServerListener + + + + large-client + org.eclipse.jetty.websocket.jsr356.server.LargeClientContainerServlet + + + + large-client + /echo/servlet + + \ No newline at end of file diff --git a/jetty-websocket/javax-websocket-server-impl/src/test/resources/large-client-container-servlet-web.xml b/jetty-websocket/javax-websocket-server-impl/src/test/resources/large-client-container-servlet-web.xml new file mode 100644 index 00000000000..11c5eee8f67 --- /dev/null +++ b/jetty-websocket/javax-websocket-server-impl/src/test/resources/large-client-container-servlet-web.xml @@ -0,0 +1,18 @@ + + + + + large-client + org.eclipse.jetty.websocket.jsr356.server.LargeClientContainerServlet + + + + large-client + /echo/servlet + + \ No newline at end of file diff --git a/jetty-websocket/javax-websocket-server-impl/src/test/resources/large-server-as-client-container-servlet-web.xml b/jetty-websocket/javax-websocket-server-impl/src/test/resources/large-server-as-client-container-servlet-web.xml new file mode 100644 index 00000000000..144326b6ed3 --- /dev/null +++ b/jetty-websocket/javax-websocket-server-impl/src/test/resources/large-server-as-client-container-servlet-web.xml @@ -0,0 +1,18 @@ + + + + + large-client + org.eclipse.jetty.websocket.jsr356.server.LargeServerContainerAsClientContainerServlet + + + + large-client + /echo/servlet + + \ No newline at end of file diff --git a/jetty-websocket/websocket-api/src/main/java/org/eclipse/jetty/websocket/api/WebSocketPolicy.java b/jetty-websocket/websocket-api/src/main/java/org/eclipse/jetty/websocket/api/WebSocketPolicy.java index da643d73857..16098e8debb 100644 --- a/jetty-websocket/websocket-api/src/main/java/org/eclipse/jetty/websocket/api/WebSocketPolicy.java +++ b/jetty-websocket/websocket-api/src/main/java/org/eclipse/jetty/websocket/api/WebSocketPolicy.java @@ -142,22 +142,42 @@ public class WebSocketPolicy } } + /** + * Make a copy of the policy, with current values. + * @return the cloned copy of the policy. + */ public WebSocketPolicy clonePolicy() { - return clonePolicy(this.behavior); + WebSocketPolicy clone = new WebSocketPolicy(this.behavior); + clone.idleTimeout = this.getIdleTimeout(); + clone.maxTextMessageSize = this.getMaxTextMessageSize(); + clone.maxTextMessageBufferSize = this.getMaxTextMessageBufferSize(); + clone.maxBinaryMessageSize = this.getMaxBinaryMessageSize(); + clone.maxBinaryMessageBufferSize = this.getMaxBinaryMessageBufferSize(); + clone.inputBufferSize = this.getInputBufferSize() ; + clone.asyncWriteTimeout = this.getAsyncWriteTimeout(); + return clone; } + /** + * Make a copy of the policy, with current values, but a different behavior. + * + * @param behavior the behavior to copy/clone + * @return the cloned policy with a new behavior. + * @deprecated use {@link #delegateAs(WebSocketBehavior)} instead + */ + @Deprecated public WebSocketPolicy clonePolicy(WebSocketBehavior behavior) { - WebSocketPolicy clone = new WebSocketPolicy(behavior); - clone.idleTimeout = this.idleTimeout; - clone.maxTextMessageSize = this.maxTextMessageSize; - clone.maxTextMessageBufferSize = this.maxTextMessageBufferSize; - clone.maxBinaryMessageSize = this.maxBinaryMessageSize; - clone.maxBinaryMessageBufferSize = this.maxBinaryMessageBufferSize; - clone.inputBufferSize = this.inputBufferSize; - clone.asyncWriteTimeout = this.asyncWriteTimeout; - return clone; + return delegateAs(behavior); + } + + public WebSocketPolicy delegateAs(WebSocketBehavior behavior) + { + if(behavior == this.behavior) + return this; + + return new WebSocketPolicy.Delegated(this, behavior); } /** @@ -369,16 +389,152 @@ public class WebSocketPolicy public String toString() { StringBuilder builder = new StringBuilder(); - builder.append("WebSocketPolicy@").append(Integer.toHexString(hashCode())); - builder.append("[behavior=").append(behavior); - builder.append(",maxTextMessageSize=").append(maxTextMessageSize); - builder.append(",maxTextMessageBufferSize=").append(maxTextMessageBufferSize); - builder.append(",maxBinaryMessageSize=").append(maxBinaryMessageSize); - builder.append(",maxBinaryMessageBufferSize=").append(maxBinaryMessageBufferSize); - builder.append(",asyncWriteTimeout=").append(asyncWriteTimeout); - builder.append(",idleTimeout=").append(idleTimeout); - builder.append(",inputBufferSize=").append(inputBufferSize); + builder.append(this.getClass().getSimpleName()); + builder.append("@").append(Integer.toHexString(hashCode())); + builder.append("[behavior=").append(getBehavior()); + builder.append(",maxTextMessageSize=").append(getMaxTextMessageSize()); + builder.append(",maxTextMessageBufferSize=").append(getMaxTextMessageBufferSize()); + builder.append(",maxBinaryMessageSize=").append(getMaxBinaryMessageSize()); + builder.append(",maxBinaryMessageBufferSize=").append(getMaxTextMessageBufferSize()); + builder.append(",asyncWriteTimeout=").append(getAsyncWriteTimeout()); + builder.append(",idleTimeout=").append(getIdleTimeout()); + builder.append(",inputBufferSize=").append(getInputBufferSize()); builder.append("]"); return builder.toString(); } + + /** + * Allows Behavior to be changed, but the settings to delegated. + *

+ * This rears its ugly head when a JSR356 Server Container is used as a + * JSR356 Client Container. + * The JSR356 Server Container is Behavior SERVER, but its container + * level Policy is shared with the JSR356 Client Container as well. + * This allows a delegate to the policy with a different behavior. + *

+ */ + private class Delegated extends WebSocketPolicy + { + private final WebSocketPolicy delegated; + + public Delegated(WebSocketPolicy policy, WebSocketBehavior behavior) + { + super(behavior); + this.delegated = policy; + } + + @Override + public void assertValidBinaryMessageSize(int requestedSize) + { + delegated.assertValidBinaryMessageSize(requestedSize); + } + + @Override + public void assertValidTextMessageSize(int requestedSize) + { + delegated.assertValidTextMessageSize(requestedSize); + } + + @Override + public WebSocketPolicy clonePolicy() + { + return delegated.clonePolicy(); + } + + @Override + public WebSocketPolicy clonePolicy(WebSocketBehavior behavior) + { + return delegated.clonePolicy(behavior); + } + + @Override + public WebSocketPolicy delegateAs(WebSocketBehavior behavior) + { + return delegated.delegateAs(behavior); + } + + @Override + public long getAsyncWriteTimeout() + { + return delegated.getAsyncWriteTimeout(); + } + + @Override + public long getIdleTimeout() + { + return delegated.getIdleTimeout(); + } + + @Override + public int getInputBufferSize() + { + return delegated.getInputBufferSize(); + } + + @Override + public int getMaxBinaryMessageBufferSize() + { + return delegated.getMaxBinaryMessageBufferSize(); + } + + @Override + public int getMaxBinaryMessageSize() + { + return delegated.getMaxBinaryMessageSize(); + } + + @Override + public int getMaxTextMessageBufferSize() + { + return delegated.getMaxTextMessageBufferSize(); + } + + @Override + public int getMaxTextMessageSize() + { + return delegated.getMaxTextMessageSize(); + } + + @Override + public void setAsyncWriteTimeout(long ms) + { + delegated.setAsyncWriteTimeout(ms); + } + + @Override + public void setIdleTimeout(long ms) + { + delegated.setIdleTimeout(ms); + } + + @Override + public void setInputBufferSize(int size) + { + delegated.setInputBufferSize(size); + } + + @Override + public void setMaxBinaryMessageBufferSize(int size) + { + delegated.setMaxBinaryMessageBufferSize(size); + } + + @Override + public void setMaxBinaryMessageSize(int size) + { + delegated.setMaxBinaryMessageSize(size); + } + + @Override + public void setMaxTextMessageBufferSize(int size) + { + delegated.setMaxTextMessageBufferSize(size); + } + + @Override + public void setMaxTextMessageSize(int size) + { + delegated.setMaxTextMessageSize(size); + } + } } diff --git a/jetty-websocket/websocket-client/src/main/java/org/eclipse/jetty/websocket/client/WebSocketClient.java b/jetty-websocket/websocket-client/src/main/java/org/eclipse/jetty/websocket/client/WebSocketClient.java index 404aa16a8c2..f34b7ad077c 100644 --- a/jetty-websocket/websocket-client/src/main/java/org/eclipse/jetty/websocket/client/WebSocketClient.java +++ b/jetty-websocket/websocket-client/src/main/java/org/eclipse/jetty/websocket/client/WebSocketClient.java @@ -269,7 +269,7 @@ public class WebSocketClient extends ContainerLifeCycle implements WebSocketCont } // Ensure we get a Client version of the policy. - this.policy = scope.getPolicy().clonePolicy(WebSocketBehavior.CLIENT); + this.policy = scope.getPolicy().delegateAs(WebSocketBehavior.CLIENT); // Support Late Binding of Object Factory (for CDI) this.objectFactorySupplier = () -> scope.getObjectFactory(); this.extensionRegistry = new WebSocketExtensionFactory(this); diff --git a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/Parser.java b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/Parser.java index fb1d22717d9..8d02ed64473 100644 --- a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/Parser.java +++ b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/Parser.java @@ -72,7 +72,6 @@ public class Parser private ByteBuffer payload; private int payloadLength; private PayloadProcessor maskProcessor = new DeMaskProcessor(); - // private PayloadProcessor strictnessProcessor; /** * Is there an extension using RSV flag?