From a703fc626b0ec8d71e9938f214660f3e30e86002 Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Tue, 27 Nov 2018 14:42:30 -0600 Subject: [PATCH 01/12] Fixes #3139 - NPE in WebSocketContainerInitializer Signed-off-by: Joakim Erdfelt --- .../org/eclipse/jetty/embedded/WebSocketJsrServer.java | 9 ++++++++- .../deploy/WebSocketServerContainerInitializer.java | 2 +- .../jetty/websocket/jsr356/server/ConfiguratorTest.java | 9 ++++++++- 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/WebSocketJsrServer.java b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/WebSocketJsrServer.java index 916727658c0..e4a0896a446 100644 --- a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/WebSocketJsrServer.java +++ b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/WebSocketJsrServer.java @@ -23,6 +23,8 @@ import javax.websocket.Session; import javax.websocket.server.ServerEndpoint; import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.handler.DefaultHandler; +import org.eclipse.jetty.server.handler.HandlerList; import org.eclipse.jetty.servlet.ServletContextHandler; import org.eclipse.jetty.websocket.jsr356.server.ServerContainer; import org.eclipse.jetty.websocket.jsr356.server.deploy.WebSocketServerContainerInitializer; @@ -49,10 +51,12 @@ public class WebSocketJsrServer { Server server = new Server(8080); + HandlerList handlers = new HandlerList(); + ServletContextHandler context = new ServletContextHandler( ServletContextHandler.SESSIONS); context.setContextPath("/"); - server.setHandler(context); + handlers.addHandler(context); // Enable javax.websocket configuration for the context ServerContainer wsContainer = WebSocketServerContainerInitializer @@ -61,6 +65,9 @@ public class WebSocketJsrServer // Add your websockets to the container wsContainer.addEndpoint(EchoJsrSocket.class); + handlers.addHandler(new DefaultHandler()); + + server.setHandler(handlers); server.start(); context.dumpStdErr(); server.join(); diff --git a/jetty-websocket/javax-websocket-server-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/server/deploy/WebSocketServerContainerInitializer.java b/jetty-websocket/javax-websocket-server-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/server/deploy/WebSocketServerContainerInitializer.java index 4c8a0d7b172..239963d620b 100644 --- a/jetty-websocket/javax-websocket-server-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/server/deploy/WebSocketServerContainerInitializer.java +++ b/jetty-websocket/javax-websocket-server-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/server/deploy/WebSocketServerContainerInitializer.java @@ -143,7 +143,7 @@ public class WebSocketServerContainerInitializer implements ServletContainerInit // Build HttpClient HttpClient httpClient = (HttpClient) context.getServletContext().getAttribute(HTTPCLIENT_ATTRIBUTE); - if(httpClient == null) + if ((httpClient == null) && (context.getServer() != null)) { httpClient = (HttpClient) context.getServer().getAttribute(HTTPCLIENT_ATTRIBUTE); } diff --git a/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/ConfiguratorTest.java b/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/ConfiguratorTest.java index 7527453c0eb..e4b60e1f19b 100644 --- a/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/ConfiguratorTest.java +++ b/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/ConfiguratorTest.java @@ -58,6 +58,8 @@ import org.eclipse.jetty.http.HttpFields; import org.eclipse.jetty.http.HttpHeader; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.ServerConnector; +import org.eclipse.jetty.server.handler.DefaultHandler; +import org.eclipse.jetty.server.handler.HandlerList; import org.eclipse.jetty.servlet.ServletContextHandler; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; @@ -403,9 +405,11 @@ public class ConfiguratorTest connector.setPort(0); server.addConnector(connector); + HandlerList handlers = new HandlerList(); + ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS); context.setContextPath("/"); - server.setHandler(context); + handlers.addHandler(context); ServerContainer container = WebSocketServerContainerInitializer.configureContext(context); container.addEndpoint(CaptureHeadersSocket.class); @@ -423,6 +427,9 @@ public class ConfiguratorTest .build(); container.addEndpoint(overrideEndpointConfig); + handlers.addHandler(new DefaultHandler()); + + server.setHandler(handlers); server.start(); String host = connector.getHost(); if (host == null) From da3d69261cd23d2826db799ba45ce551dc73881e Mon Sep 17 00:00:00 2001 From: Simone Bordet Date: Fri, 4 Jan 2019 21:27:39 +0100 Subject: [PATCH 02/12] Fixes #3240 - ALPN support for Java 13. --- .../src/main/config/modules/alpn-impl/alpn-13.mod | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 jetty-alpn/jetty-alpn-server/src/main/config/modules/alpn-impl/alpn-13.mod diff --git a/jetty-alpn/jetty-alpn-server/src/main/config/modules/alpn-impl/alpn-13.mod b/jetty-alpn/jetty-alpn-server/src/main/config/modules/alpn-impl/alpn-13.mod new file mode 100644 index 00000000000..689601a4197 --- /dev/null +++ b/jetty-alpn/jetty-alpn-server/src/main/config/modules/alpn-impl/alpn-13.mod @@ -0,0 +1,4 @@ +DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html + +[depend] +alpn-impl/alpn-9 From 0078d8aed6874defdc18f2dd5460a9b776ad9044 Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Tue, 8 Jan 2019 15:45:54 -0600 Subject: [PATCH 03/12] Issue #3242 - Updating BrowserDebugTool to help troubleshoot issue Signed-off-by: Joakim Erdfelt --- .../server/browser/BrowserDebugTool.java | 21 +++++++++++++++---- .../server/browser/BrowserSocket.java | 6 ++++++ .../resources/browser-debug-tool/index.html | 4 ++-- .../resources/browser-debug-tool/websocket.js | 2 +- 4 files changed, 26 insertions(+), 7 deletions(-) diff --git a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/browser/BrowserDebugTool.java b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/browser/BrowserDebugTool.java index f1645badb96..5dfba99464a 100644 --- a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/browser/BrowserDebugTool.java +++ b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/browser/BrowserDebugTool.java @@ -18,14 +18,22 @@ package org.eclipse.jetty.websocket.server.browser; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URISyntaxException; +import java.nio.file.Path; import java.util.ArrayList; import java.util.List; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.ServerConnector; import org.eclipse.jetty.server.handler.ResourceHandler; +import org.eclipse.jetty.toolchain.test.MavenTestingUtils; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; +import org.eclipse.jetty.util.resource.PathResource; +import org.eclipse.jetty.util.resource.Resource; import org.eclipse.jetty.websocket.api.extensions.ExtensionConfig; import org.eclipse.jetty.websocket.common.extensions.FrameCaptureExtension; import org.eclipse.jetty.websocket.server.WebSocketHandler; @@ -120,8 +128,7 @@ public class BrowserDebugTool implements WebSocketCreator return connector.getLocalPort(); } - public void prepare(int port) - { + public void prepare(int port) throws IOException, URISyntaxException { server = new Server(); connector = new ServerConnector(server); connector.setPort(port); @@ -150,16 +157,22 @@ public class BrowserDebugTool implements WebSocketCreator server.setHandler(wsHandler); - String resourceBase = "src/test/resources/browser-debug-tool"; + Resource staticResourceBase = findStaticResources(); ResourceHandler rHandler = new ResourceHandler(); rHandler.setDirectoriesListed(true); - rHandler.setResourceBase(resourceBase); + rHandler.setBaseResource(staticResourceBase); wsHandler.setHandler(rHandler); LOG.info("{} setup on port {}",this.getClass().getName(),port); } + private Resource findStaticResources() throws FileNotFoundException, URISyntaxException, MalformedURLException { + Path path = MavenTestingUtils.getTestResourcePathDir("browser-debug-tool"); + LOG.info("Static Resources: {}", path); + return new PathResource(path); + } + public void start() throws Exception { server.start(); diff --git a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/browser/BrowserSocket.java b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/browser/BrowserSocket.java index 24828ac6724..a27d566cd80 100644 --- a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/browser/BrowserSocket.java +++ b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/browser/BrowserSocket.java @@ -39,6 +39,7 @@ import org.eclipse.jetty.websocket.api.annotations.OnWebSocketConnect; import org.eclipse.jetty.websocket.api.annotations.OnWebSocketError; import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage; import org.eclipse.jetty.websocket.api.annotations.WebSocket; +import org.eclipse.jetty.websocket.common.WebSocketSession; @WebSocket public class BrowserSocket @@ -226,6 +227,11 @@ public class BrowserSocket writeMessage("Server time: %s",sdf.format(now.getTime())); break; } + case "dump": + { + ((WebSocketSession)session).dumpStdErr(); + break; + } default: { writeMessage("key[%s] val[%s]",key,val); diff --git a/jetty-websocket/websocket-server/src/test/resources/browser-debug-tool/index.html b/jetty-websocket/websocket-server/src/test/resources/browser-debug-tool/index.html index 4ffb6e5cc63..b7b638d3ba1 100644 --- a/jetty-websocket/websocket-server/src/test/resources/browser-debug-tool/index.html +++ b/jetty-websocket/websocket-server/src/test/resources/browser-debug-tool/index.html @@ -16,8 +16,8 @@ + - @@ -32,7 +32,7 @@ $("manythreads").onclick = function(event) {wstool.write("manythreads:20,25,60"); return false; } $("hello").onclick = function(event) {wstool.write("Hello"); return false; } $("there").onclick = function(event) {wstool.write("There"); return false; } - $("twain").onclick = function(event) {wstool.write("@twain.txt"); return false; } + $("dump").onclick = function(event) {wstool.write("dump:"); return false; } $("json").onclick = function(event) {wstool.write("[{\"channel\":\"/meta/subscribe\",\"subscription\":\"/chat/demo\",\"id\":\"2\",\"clientId\":\"81dwnxwbgs0h0bq8968b0a0gyl\",\"timestamp\":\"Thu," + " 12 Sep 2013 19:42:30 GMT\"},{\"channel\":\"/meta/subscribe\",\"subscription\":\"/members/demo\",\"id\":\"3\",\"clientId\":\"81dwnxwbgs0h0bq8968b0a0gyl\",\"timestamp\":\"Thu," + " 12 Sep 2013 19:42:30 GMT\"},{\"channel\":\"/chat/demo\",\"data\":{\"user\":\"ch\",\"membership\":\"join\",\"chat\":\"ch" diff --git a/jetty-websocket/websocket-server/src/test/resources/browser-debug-tool/websocket.js b/jetty-websocket/websocket-server/src/test/resources/browser-debug-tool/websocket.js index 03f2896d4cc..66a1ec0ee52 100644 --- a/jetty-websocket/websocket-server/src/test/resources/browser-debug-tool/websocket.js +++ b/jetty-websocket/websocket-server/src/test/resources/browser-debug-tool/websocket.js @@ -84,8 +84,8 @@ var wstool = { $('manythreads').disabled = !enabled; $('hello').disabled = !enabled; $('there').disabled = !enabled; + $('dump').disabled = !enabled; $('json').disabled = !enabled; - $('twain').disabled = !enabled; $('send10k').disabled = !enabled; $('send100k').disabled = !enabled; $('send1000k').disabled = !enabled; From 87c82ae64459002179dd00135bb85d0059529894 Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Tue, 8 Jan 2019 15:47:17 -0600 Subject: [PATCH 04/12] Issue #3242 - Allow Dumpable to control how self is represented + Do not rely on Object.toString() for representing self (aka:this) in the dump output. Using Object.toString() can result in lots of duplicated information in the dump (and super long lines) Signed-off-by: Joakim Erdfelt --- .../org/eclipse/jetty/util/component/Dumpable.java | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/component/Dumpable.java b/jetty-util/src/main/java/org/eclipse/jetty/util/component/Dumpable.java index 748c678167f..37efb48a2e9 100644 --- a/jetty-util/src/main/java/org/eclipse/jetty/util/component/Dumpable.java +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/component/Dumpable.java @@ -70,6 +70,15 @@ public interface Dumpable return b.toString(); } + /** + * The description of this/self found in the dump. + * Allows for alternative representation of Object other then .toString() + * where the long form output of toString() is represented in a cleaner way + * within the dump infrastructure. + * + * @return the representation of self + */ + default String dumpSelf() { return toString(); } /** * Dump just an Object (but not it's contained items) to an Appendable. @@ -90,6 +99,8 @@ public interface Dumpable s = String.format("%s@%x[size=%d]",o.getClass().getComponentType(),o.hashCode(), Array.getLength(o)); else if (o instanceof Map) s = String.format("%s@%x{size=%d}",o.getClass().getName(),o.hashCode(),((Map)o).size()); + else if (o instanceof Dumpable) + s = ((Dumpable)o).dumpSelf().replace("\r\n","|").replace("\n","|"); else s = String.valueOf(o).replace("\r\n","|").replace("\n","|"); From 689fab4979c515900c2b0e2c226aa7797c8b46cb Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Tue, 8 Jan 2019 15:47:49 -0600 Subject: [PATCH 05/12] Issue #3242 - Cleaning up WebSocket dump Signed-off-by: Joakim Erdfelt --- .../websocket/common/WebSocketSession.java | 13 +++++++---- .../common/extensions/AbstractExtension.java | 23 +------------------ .../common/extensions/ExtensionStack.java | 17 +++----------- .../io/AbstractWebSocketConnection.java | 16 +++++++------ 4 files changed, 21 insertions(+), 48 deletions(-) diff --git a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/WebSocketSession.java b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/WebSocketSession.java index d3e339a3950..36ffdf9f65d 100644 --- a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/WebSocketSession.java +++ b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/WebSocketSession.java @@ -37,6 +37,7 @@ import org.eclipse.jetty.util.Callback; import org.eclipse.jetty.util.annotation.ManagedAttribute; import org.eclipse.jetty.util.annotation.ManagedObject; import org.eclipse.jetty.util.component.ContainerLifeCycle; +import org.eclipse.jetty.util.component.Dumpable; import org.eclipse.jetty.util.component.DumpableCollection; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; @@ -277,11 +278,13 @@ public class WebSocketSession extends ContainerLifeCycle implements Session, Rem } @Override - public void dump(Appendable out, String indent) throws IOException - { - dumpObjects(out,indent, - DumpableCollection.from("incoming", incomingHandler), - DumpableCollection.from("outgoing", outgoingHandler)); + public String dumpSelf() { + return String.format("%s@%x[behavior=%s,batchMode=%s,idleTimeout=%,d,requestURI=%s]", + this.getClass().getSimpleName(), hashCode(), + getPolicy().getBehavior(), + getBatchMode(), + getIdleTimeout(), + getRequestURI()); } @Override diff --git a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/extensions/AbstractExtension.java b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/extensions/AbstractExtension.java index 013cc9aebdf..0013ceaaa40 100644 --- a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/extensions/AbstractExtension.java +++ b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/extensions/AbstractExtension.java @@ -39,7 +39,7 @@ import org.eclipse.jetty.websocket.common.LogicalConnection; import org.eclipse.jetty.websocket.common.scopes.WebSocketContainerScope; @ManagedObject("Abstract Extension") -public abstract class AbstractExtension extends AbstractLifeCycle implements Dumpable, Extension +public abstract class AbstractExtension extends AbstractLifeCycle implements Extension { private final Logger log; private WebSocketPolicy policy; @@ -54,27 +54,6 @@ public abstract class AbstractExtension extends AbstractLifeCycle implements Dum log = Log.getLogger(this.getClass()); } - @Override - public String dump() - { - return Dumpable.dump(this); - } - - @Override - public void dump(Appendable out, String indent) throws IOException - { - // incoming - dumpWithHeading(out, indent, "incoming", this.nextIncoming); - dumpWithHeading(out, indent, "outgoing", this.nextOutgoing); - } - - protected void dumpWithHeading(Appendable out, String indent, String heading, Object bean) throws IOException - { - out.append(indent).append(" +- "); - out.append(heading).append(" : "); - out.append(bean.toString()); - } - @Deprecated public void init(WebSocketContainerScope container) { diff --git a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/extensions/ExtensionStack.java b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/extensions/ExtensionStack.java index d720869555b..083db32cc6f 100644 --- a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/extensions/ExtensionStack.java +++ b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/extensions/ExtensionStack.java @@ -29,6 +29,7 @@ import org.eclipse.jetty.util.IteratingCallback; import org.eclipse.jetty.util.annotation.ManagedAttribute; import org.eclipse.jetty.util.annotation.ManagedObject; import org.eclipse.jetty.util.component.ContainerLifeCycle; +import org.eclipse.jetty.util.component.DumpableCollection; import org.eclipse.jetty.util.component.LifeCycle; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; @@ -108,20 +109,8 @@ public class ExtensionStack extends ContainerLifeCycle implements IncomingFrames } @Override - public void dump(Appendable out, String indent) throws IOException - { - super.dump(out,indent); - - IncomingFrames websocket = getLastIncoming(); - OutgoingFrames network = getLastOutgoing(); - - out.append(indent).append(" +- Stack").append(System.lineSeparator()); - out.append(indent).append(" +- Network : ").append(network.toString()).append(System.lineSeparator()); - for (Extension ext : extensions) - { - out.append(indent).append(" +- Extension: ").append(ext.toString()).append(System.lineSeparator()); - } - out.append(indent).append(" +- Websocket: ").append(websocket.toString()).append(System.lineSeparator()); + public String dumpSelf() { + return String.format("ExtensionStack%x [size=%d]", hashCode(), extensions.size()); } @ManagedAttribute(name = "Extension List", readonly = true) diff --git a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/io/AbstractWebSocketConnection.java b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/io/AbstractWebSocketConnection.java index eb4efef26ab..0eff7ed1580 100644 --- a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/io/AbstractWebSocketConnection.java +++ b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/io/AbstractWebSocketConnection.java @@ -30,6 +30,7 @@ import java.util.concurrent.atomic.AtomicLong; import org.eclipse.jetty.io.AbstractConnection; import org.eclipse.jetty.io.ByteBufferPool; +import org.eclipse.jetty.io.ChannelEndPoint; import org.eclipse.jetty.io.Connection; import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.util.BufferUtil; @@ -631,15 +632,16 @@ public abstract class AbstractWebSocketConnection extends AbstractConnection imp } @Override - public String dump() - { - return Dumpable.dump(this); + public String dumpSelf() { + return String.format("%s@%x", this.getClass().getSimpleName(), hashCode()); } - @Override - public void dump(Appendable out, String indent) throws IOException - { - out.append(toString()).append(System.lineSeparator()); + public void dump(Appendable out, String indent) throws IOException { + Object endpRef = toConnectionString(); + EndPoint endp = getEndPoint(); + if(endp instanceof ChannelEndPoint) + endpRef = ((ChannelEndPoint) endp).toEndPointString(); + Dumpable.dumpObjects(out, indent, this, endpRef, ioState, flusher, generator, parser); } @Override From aad5e9733638c7e83c34e62f1f2d4d1e77a17f60 Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Tue, 8 Jan 2019 15:48:22 -0600 Subject: [PATCH 06/12] Issue #3242 - Relying on EndPoint.toString() for ProxyEndPoint.toString() Signed-off-by: Joakim Erdfelt --- .../org/eclipse/jetty/server/ProxyConnectionFactory.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/ProxyConnectionFactory.java b/jetty-server/src/main/java/org/eclipse/jetty/server/ProxyConnectionFactory.java index 5436006635f..9b3aef0d17b 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/ProxyConnectionFactory.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/ProxyConnectionFactory.java @@ -620,6 +620,11 @@ public class ProxyConnectionFactory extends AbstractConnectionFactory return _remote; } + @Override + public String toString() { + return _endp.toString(); + } + @Override public boolean isOpen() { From ce3defa2f5cdc588b122fba18e34fede93803da7 Mon Sep 17 00:00:00 2001 From: Greg Wilkins Date: Thu, 10 Jan 2019 09:07:02 +1100 Subject: [PATCH 07/12] fixed debug Signed-off-by: Greg Wilkins --- .../main/java/org/eclipse/jetty/proxy/AbstractProxyServlet.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jetty-proxy/src/main/java/org/eclipse/jetty/proxy/AbstractProxyServlet.java b/jetty-proxy/src/main/java/org/eclipse/jetty/proxy/AbstractProxyServlet.java index ad44afff3e7..5322c215657 100644 --- a/jetty-proxy/src/main/java/org/eclipse/jetty/proxy/AbstractProxyServlet.java +++ b/jetty-proxy/src/main/java/org/eclipse/jetty/proxy/AbstractProxyServlet.java @@ -549,7 +549,7 @@ public abstract class AbstractProxyServlet extends HttpServlet } builder.append(System.lineSeparator()); - _log.debug("{} proxying to upstream:{}{}{}{}", + _log.debug("{} proxying to upstream:{}{}{}{}{}", getRequestId(clientRequest), System.lineSeparator(), builder, From 69dc0059d18251cacdad1b360baf6ffca3dbf5e4 Mon Sep 17 00:00:00 2001 From: Olivier Lamy Date: Thu, 10 Jan 2019 22:12:12 +1300 Subject: [PATCH 08/12] use jackson-databind 2.9.8 (#3252) Signed-off-by: olivier lamy --- jetty-maven-plugin/src/it/it-parent-pom/pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/jetty-maven-plugin/src/it/it-parent-pom/pom.xml b/jetty-maven-plugin/src/it/it-parent-pom/pom.xml index 2ad7f139b01..ebae8653539 100644 --- a/jetty-maven-plugin/src/it/it-parent-pom/pom.xml +++ b/jetty-maven-plugin/src/it/it-parent-pom/pom.xml @@ -39,7 +39,7 @@ com.fasterxml.jackson.core jackson-databind - 2.9.7 + 2.9.8 org.slf4j @@ -144,4 +144,4 @@ - \ No newline at end of file + From 478e5ac5938627bb643599640a67ddd2dc19460b Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Fri, 11 Jan 2019 10:16:46 -0600 Subject: [PATCH 09/12] Issue #3242 - Post-review cleanup of WebSocket dump Signed-off-by: Joakim Erdfelt --- .../org/eclipse/jetty/websocket/common/WebSocketSession.java | 2 +- .../jetty/websocket/common/extensions/ExtensionStack.java | 2 +- .../org/eclipse/jetty/websocket/common/io/FrameFlusher.java | 5 ++--- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/WebSocketSession.java b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/WebSocketSession.java index 36ffdf9f65d..928dc0664c1 100644 --- a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/WebSocketSession.java +++ b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/WebSocketSession.java @@ -279,7 +279,7 @@ public class WebSocketSession extends ContainerLifeCycle implements Session, Rem @Override public String dumpSelf() { - return String.format("%s@%x[behavior=%s,batchMode=%s,idleTimeout=%,d,requestURI=%s]", + return String.format("%s@%x[behavior=%s,batchMode=%s,idleTimeout=%d,requestURI=%s]", this.getClass().getSimpleName(), hashCode(), getPolicy().getBehavior(), getBatchMode(), diff --git a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/extensions/ExtensionStack.java b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/extensions/ExtensionStack.java index 083db32cc6f..324dfbd2c20 100644 --- a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/extensions/ExtensionStack.java +++ b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/extensions/ExtensionStack.java @@ -110,7 +110,7 @@ public class ExtensionStack extends ContainerLifeCycle implements IncomingFrames @Override public String dumpSelf() { - return String.format("ExtensionStack%x [size=%d]", hashCode(), extensions.size()); + return String.format("%s@%x[size=%d,queueSize=%d]", getClass().getSimpleName(), hashCode(), extensions.size(), getQueueSize()); } @ManagedAttribute(name = "Extension List", readonly = true) diff --git a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/io/FrameFlusher.java b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/io/FrameFlusher.java index 65cbadf5cdd..8e19d3bb123 100644 --- a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/io/FrameFlusher.java +++ b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/io/FrameFlusher.java @@ -337,9 +337,8 @@ public class FrameFlusher extends IteratingCallback ByteBuffer agg = aggregate; if (agg != null) aggSize = agg.position(); - return String.format("%s@%x[queueSize=%d,aggregateSize=%d,terminated=%s]", - getClass().getSimpleName(), - hashCode(), + return String.format("%s[queueSize=%d,aggregateSize=%d,terminated=%s]", + super.toString(), getQueueSize(), aggSize, terminated); From e9143725b0d7803ccf12eca9ea2a5ed3661b697c Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Fri, 11 Jan 2019 10:23:51 -0600 Subject: [PATCH 10/12] Issue #3242 - Post-review cleanup of ProxyEndPoint Signed-off-by: Joakim Erdfelt --- .../org/eclipse/jetty/server/ProxyConnectionFactory.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/ProxyConnectionFactory.java b/jetty-server/src/main/java/org/eclipse/jetty/server/ProxyConnectionFactory.java index 9b3aef0d17b..49d1b3542b0 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/ProxyConnectionFactory.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/ProxyConnectionFactory.java @@ -622,7 +622,12 @@ public class ProxyConnectionFactory extends AbstractConnectionFactory @Override public String toString() { - return _endp.toString(); + return String.format("%s@%x[remote=%s,local=%s,endpoint=%s]", + getClass().getSimpleName(), + hashCode(), + _remote, + _local, + _endp); } @Override From 1b7ae00be3fd7db2a5906250718c09300c06ec1b Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Fri, 11 Jan 2019 11:27:10 -0600 Subject: [PATCH 11/12] Issue #3242 - Post-review cleanup of AbstractWebSocketConnection dump of EndPoint Signed-off-by: Joakim Erdfelt --- .../websocket/common/io/AbstractWebSocketConnection.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/io/AbstractWebSocketConnection.java b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/io/AbstractWebSocketConnection.java index 0eff7ed1580..f9ff4174fa7 100644 --- a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/io/AbstractWebSocketConnection.java +++ b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/io/AbstractWebSocketConnection.java @@ -29,8 +29,8 @@ import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicLong; import org.eclipse.jetty.io.AbstractConnection; +import org.eclipse.jetty.io.AbstractEndPoint; import org.eclipse.jetty.io.ByteBufferPool; -import org.eclipse.jetty.io.ChannelEndPoint; import org.eclipse.jetty.io.Connection; import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.util.BufferUtil; @@ -637,10 +637,10 @@ public abstract class AbstractWebSocketConnection extends AbstractConnection imp } public void dump(Appendable out, String indent) throws IOException { - Object endpRef = toConnectionString(); EndPoint endp = getEndPoint(); - if(endp instanceof ChannelEndPoint) - endpRef = ((ChannelEndPoint) endp).toEndPointString(); + Object endpRef = endp.toString(); + if(endp instanceof AbstractEndPoint) + endpRef = ((AbstractEndPoint) endp).toEndPointString(); Dumpable.dumpObjects(out, indent, this, endpRef, ioState, flusher, generator, parser); } From c320e020d2fbdb458b692b38843d705de1f6aaea Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Mon, 14 Jan 2019 10:13:59 -0600 Subject: [PATCH 12/12] Working BrowserDebugTool for jetty websocket API Signed-off-by: Joakim Erdfelt --- .../common/WebSocketSessionImpl.java | 19 +- .../jetty-websocket-server/pom.xml | 5 + .../server/browser/BrowserDebugTool.java | 202 ++++++++++++ .../server/browser/BrowserSocket.java | 291 ++++++++++++++++++ .../resources/browser-debug-tool/index.html | 58 ++++ .../resources/browser-debug-tool/main.css | 29 ++ .../resources/browser-debug-tool/websocket.js | 141 +++++++++ .../core/internal/FrameCaptureExtension.java | 3 + .../servlet/WebSocketServletFactory.java | 3 + 9 files changed, 750 insertions(+), 1 deletion(-) create mode 100644 jetty-websocket/jetty-websocket-server/src/test/java/org/eclipse/jetty/websocket/server/browser/BrowserDebugTool.java create mode 100644 jetty-websocket/jetty-websocket-server/src/test/java/org/eclipse/jetty/websocket/server/browser/BrowserSocket.java create mode 100644 jetty-websocket/jetty-websocket-server/src/test/resources/browser-debug-tool/index.html create mode 100644 jetty-websocket/jetty-websocket-server/src/test/resources/browser-debug-tool/main.css create mode 100644 jetty-websocket/jetty-websocket-server/src/test/resources/browser-debug-tool/websocket.js diff --git a/jetty-websocket/jetty-websocket-common/src/main/java/org/eclipse/jetty/websocket/common/WebSocketSessionImpl.java b/jetty-websocket/jetty-websocket-common/src/main/java/org/eclipse/jetty/websocket/common/WebSocketSessionImpl.java index 4bbec889089..b332b792444 100644 --- a/jetty-websocket/jetty-websocket-common/src/main/java/org/eclipse/jetty/websocket/common/WebSocketSessionImpl.java +++ b/jetty-websocket/jetty-websocket-common/src/main/java/org/eclipse/jetty/websocket/common/WebSocketSessionImpl.java @@ -18,6 +18,7 @@ package org.eclipse.jetty.websocket.common; +import org.eclipse.jetty.util.component.Dumpable; import org.eclipse.jetty.websocket.api.CloseStatus; import org.eclipse.jetty.websocket.api.Session; import org.eclipse.jetty.websocket.api.SuspendToken; @@ -26,11 +27,12 @@ import org.eclipse.jetty.websocket.api.UpgradeResponse; import org.eclipse.jetty.websocket.api.WebSocketBehavior; import org.eclipse.jetty.websocket.core.FrameHandler; +import java.io.IOException; import java.net.SocketAddress; import java.time.Duration; import java.util.Objects; -public class WebSocketSessionImpl implements Session +public class WebSocketSessionImpl implements Session, Dumpable { private final FrameHandler.CoreSession coreSession; private final JettyWebSocketFrameHandler frameHandler; @@ -204,6 +206,21 @@ public class WebSocketSessionImpl implements Session return null; } + @Override + public void dump(Appendable out, String indent) throws IOException + { + Dumpable.dumpObjects(out, indent, this, upgradeRequest, coreSession, remoteEndpoint, frameHandler); + } + + @Override + public String dumpSelf() + { + return String.format("%s@%x[behavior=%s,idleTimeout=%d]", + this.getClass().getSimpleName(), hashCode(), + getPolicy().getBehavior(), + getIdleTimeout()); + } + @Override public String toString() { diff --git a/jetty-websocket/jetty-websocket-server/pom.xml b/jetty-websocket/jetty-websocket-server/pom.xml index 8a9be182b40..b9dd5641519 100644 --- a/jetty-websocket/jetty-websocket-server/pom.xml +++ b/jetty-websocket/jetty-websocket-server/pom.xml @@ -51,6 +51,11 @@ javax.servlet javax.servlet-api + + org.eclipse.jetty.toolchain + jetty-test-helper + test + diff --git a/jetty-websocket/jetty-websocket-server/src/test/java/org/eclipse/jetty/websocket/server/browser/BrowserDebugTool.java b/jetty-websocket/jetty-websocket-server/src/test/java/org/eclipse/jetty/websocket/server/browser/BrowserDebugTool.java new file mode 100644 index 00000000000..cd712ed06ed --- /dev/null +++ b/jetty-websocket/jetty-websocket-server/src/test/java/org/eclipse/jetty/websocket/server/browser/BrowserDebugTool.java @@ -0,0 +1,202 @@ +// +// ======================================================================== +// Copyright (c) 1995-2019 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== +// + +package org.eclipse.jetty.websocket.server.browser; + +import java.io.IOException; +import java.net.URISyntaxException; +import java.nio.file.Path; +import java.time.Duration; +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.jetty.http.pathmap.ServletPathSpec; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.ServerConnector; +import org.eclipse.jetty.server.handler.DefaultHandler; +import org.eclipse.jetty.server.handler.HandlerList; +import org.eclipse.jetty.servlet.DefaultServlet; +import org.eclipse.jetty.servlet.ServletContextHandler; +import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.toolchain.test.MavenTestingUtils; +import org.eclipse.jetty.util.log.Log; +import org.eclipse.jetty.util.log.Logger; +import org.eclipse.jetty.util.resource.PathResource; +import org.eclipse.jetty.util.resource.Resource; +import org.eclipse.jetty.websocket.core.ExtensionConfig; +import org.eclipse.jetty.websocket.core.internal.FrameCaptureExtension; +import org.eclipse.jetty.websocket.server.JettyWebSocketServletContainerInitializer; +import org.eclipse.jetty.websocket.servlet.ServletUpgradeRequest; +import org.eclipse.jetty.websocket.servlet.ServletUpgradeResponse; +import org.eclipse.jetty.websocket.servlet.WebSocketCreator; +import org.eclipse.jetty.websocket.servlet.WebSocketServlet; +import org.eclipse.jetty.websocket.servlet.WebSocketServletFactory; + +/** + * Tool to help debug websocket circumstances reported around browsers. + *

+ * Provides a server, with a few simple websocket's that can be twiddled from a browser. This helps with setting up breakpoints and whatnot to help debug our + * websocket implementation from the context of a browser client. + */ +public class BrowserDebugTool +{ + private static final Logger LOG = Log.getLogger(BrowserDebugTool.class); + + public static void main(String[] args) + { + int port = 8080; + + for (int i = 0; i < args.length; i++) + { + String a = args[i]; + if ("-p".equals(a) || "--port".equals(a)) + { + port = Integer.parseInt(args[++i]); + } + } + + try + { + BrowserDebugTool tool = new BrowserDebugTool(); + tool.prepare(port); + tool.start(); + } + catch (Throwable t) + { + LOG.warn(t); + } + } + + private Server server; + private ServerConnector connector; + + public int getPort() + { + return connector.getLocalPort(); + } + + public void prepare(int port) throws IOException, URISyntaxException { + server = new Server(); + connector = new ServerConnector(server); + connector.setPort(port); + server.addConnector(connector); + + ServletContextHandler context = new ServletContextHandler(); + + JettyWebSocketServletContainerInitializer.configure(context); + + context.setContextPath("/"); + Resource staticResourceBase = findStaticResources(); + context.setBaseResource(staticResourceBase); + context.addServlet(BrowserSocketServlet.class, "/*"); + ServletHolder defHolder = new ServletHolder("default", DefaultServlet.class); + context.addServlet(defHolder, "/"); + + HandlerList handlers = new HandlerList(); + handlers.addHandler(context); + handlers.addHandler(new DefaultHandler()); + + server.setHandler(handlers); + + LOG.info("{} setup on port {}",this.getClass().getName(),port); + } + + private Resource findStaticResources() + { + Path path = MavenTestingUtils.getTestResourcePathDir("browser-debug-tool"); + LOG.info("Static Resources: {}", path); + return new PathResource(path); + } + + public void start() throws Exception + { + server.start(); + LOG.info("Server available on port {}",getPort()); + } + + public void stop() throws Exception + { + server.stop(); + } + + public static class BrowserSocketServlet extends WebSocketServlet + { + @Override + public void configure(WebSocketServletFactory factory) { + LOG.debug("Configuring WebSocketServerFactory ..."); + + // Registering Frame Debug + factory.getExtensionRegistry().register("@frame-capture", FrameCaptureExtension.class); + + // Setup the desired Socket to use for all incoming upgrade requests + factory.addMapping(new ServletPathSpec("/"), new BrowserSocketCreator()); + + // Set the timeout + factory.setDefaultIdleTimeout(Duration.ofSeconds(30)); + + // Set top end message size + factory.setDefaultMaxTextMessageSize(15 * 1024 * 1024); + } + } + + public static class BrowserSocketCreator implements WebSocketCreator + { + @Override + public Object createWebSocket(ServletUpgradeRequest req, ServletUpgradeResponse resp) + { + LOG.debug("Creating BrowserSocket"); + + if (req.getSubProtocols() != null) + { + if (!req.getSubProtocols().isEmpty()) + { + String subProtocol = req.getSubProtocols().get(0); + resp.setAcceptedSubProtocol(subProtocol); + } + } + + String ua = req.getHeader("User-Agent"); + String rexts = req.getHeader("Sec-WebSocket-Extensions"); + + // manually negotiate extensions + List negotiated = new ArrayList<>(); + // adding frame debug + negotiated.add(new ExtensionConfig("@frame-capture; output-dir=target")); + for (ExtensionConfig config : req.getExtensions()) + { + if (config.getName().equals("permessage-deflate")) + { + // what we are interested in here + negotiated.add(config); + continue; + } + // skip all others + } + + resp.setExtensions(negotiated); + + LOG.debug("User-Agent: {}",ua); + LOG.debug("Sec-WebSocket-Extensions (Request) : {}",rexts); + LOG.debug("Sec-WebSocket-Protocol (Request): {}",req.getHeader("Sec-WebSocket-Protocol")); + LOG.debug("Sec-WebSocket-Protocol (Response): {}",resp.getAcceptedSubProtocol()); + + req.getExtensions(); + return new BrowserSocket(ua,rexts); + } + } +} diff --git a/jetty-websocket/jetty-websocket-server/src/test/java/org/eclipse/jetty/websocket/server/browser/BrowserSocket.java b/jetty-websocket/jetty-websocket-server/src/test/java/org/eclipse/jetty/websocket/server/browser/BrowserSocket.java new file mode 100644 index 00000000000..b7fa0006909 --- /dev/null +++ b/jetty-websocket/jetty-websocket-server/src/test/java/org/eclipse/jetty/websocket/server/browser/BrowserSocket.java @@ -0,0 +1,291 @@ +// +// ======================================================================== +// Copyright (c) 1995-2019 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== +// + +package org.eclipse.jetty.websocket.server.browser; + +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Calendar; +import java.util.Locale; +import java.util.Random; + +import org.eclipse.jetty.util.IO; +import org.eclipse.jetty.util.Loader; +import org.eclipse.jetty.util.StringUtil; +import org.eclipse.jetty.util.component.Dumpable; +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.annotations.OnWebSocketClose; +import org.eclipse.jetty.websocket.api.annotations.OnWebSocketConnect; +import org.eclipse.jetty.websocket.api.annotations.OnWebSocketError; +import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage; +import org.eclipse.jetty.websocket.api.annotations.WebSocket; + +@WebSocket +public class BrowserSocket +{ + private static class WriteMany implements Runnable + { + private RemoteEndpoint remote; + private int size; + private int count; + + public WriteMany(RemoteEndpoint remote, int size, int count) + { + this.remote = remote; + this.size = size; + this.count = count; + } + + @Override + public void run() + { + char letters[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-|{}[]():".toCharArray(); + int lettersLen = letters.length; + char randomText[] = new char[size]; + Random rand = new Random(42); + String msg; + + for (int n = 0; n < count; n++) + { + // create random text + for (int i = 0; i < size; i++) + { + randomText[i] = letters[rand.nextInt(lettersLen)]; + } + msg = String.format("ManyThreads [%s]",String.valueOf(randomText)); + remote.sendString(msg,null); + } + } + } + + private static final Logger LOG = Log.getLogger(BrowserSocket.class); + + private Session session; + private final String userAgent; + private final String requestedExtensions; + + public BrowserSocket(String ua, String reqExts) + { + this.userAgent = ua; + this.requestedExtensions = reqExts; + } + + @OnWebSocketConnect + public void onConnect(Session session) + { + LOG.info("Connect [{}]",session); + this.session = session; + } + + @OnWebSocketClose + public void onDisconnect(int statusCode, String reason) + { + this.session = null; + LOG.info("Closed [{}, {}]",statusCode,reason); + } + + @OnWebSocketError + public void onError(Throwable cause) + { + this.session = null; + LOG.warn("Error",cause); + } + + @OnWebSocketMessage + public void onTextMessage(String message) + { + if (message.length() > 300) + { + int len = message.length(); + LOG.info("onTextMessage({} ... {}) size:{}",message.substring(0,15),message.substring(len - 15,len).replaceAll("[\r\n]*",""),len); + } + else + { + LOG.info("onTextMessage({})",message); + } + + // Is multi-line? + if (message.contains("\n")) + { + // echo back exactly + writeMessage(message); + return; + } + + // Is resource lookup? + if (message.charAt(0) == '@') + { + String name = message.substring(1); + URL url = Loader.getResource(name); + if (url == null) + { + writeMessage("Unable to find resource: " + name); + return; + } + try (InputStream in = url.openStream()) + { + String data = IO.toString(in); + writeMessage(data); + } + catch (IOException e) + { + writeMessage("Unable to read resource: " + name); + LOG.warn("Unable to read resource: " + name,e); + } + return; + } + + // Is parameterized? + int idx = message.indexOf(':'); + if (idx > 0) + { + String key = message.substring(0,idx).toLowerCase(Locale.ENGLISH); + String val = message.substring(idx + 1); + switch (key) + { + case "info": + { + if (StringUtil.isBlank(userAgent)) + { + writeMessage("Client has no User-Agent"); + } + else + { + writeMessage("Client User-Agent: " + this.userAgent); + } + + if (StringUtil.isBlank(requestedExtensions)) + { + writeMessage("Client requested no Sec-WebSocket-Extensions"); + } + else + { + writeMessage("Client requested Sec-WebSocket-Extensions: " + this.requestedExtensions); + writeMessage("Negotiated Sec-WebSocket-Extensions: " + session.getUpgradeResponse().getHeader("Sec-WebSocket-Extensions")); + } + + break; + } + case "many": + { + String parts[] = val.split(","); + int size = Integer.parseInt(parts[0]); + int count = Integer.parseInt(parts[1]); + + writeManyAsync(size,count); + break; + } + case "manythreads": + { + String parts[] = val.split(","); + int threadCount = Integer.parseInt(parts[0]); + int size = Integer.parseInt(parts[1]); + int count = Integer.parseInt(parts[2]); + + Thread threads[] = new Thread[threadCount]; + + // Setup threads + for (int n = 0; n < threadCount; n++) + { + threads[n] = new Thread(new WriteMany(session.getRemote(),size,count),"WriteMany[" + n + "]"); + } + + // Execute threads + for (Thread thread : threads) + { + thread.start(); + } + + // Drop out of this thread + break; + } + case "time": + { + Calendar now = Calendar.getInstance(); + DateFormat sdf = SimpleDateFormat.getDateTimeInstance(SimpleDateFormat.FULL,SimpleDateFormat.FULL); + writeMessage("Server time: %s",sdf.format(now.getTime())); + break; + } + case "dump": + { + if(session instanceof Dumpable) { + System.err.println(((Dumpable) session).dump()); + } else { + System.err.println(session.toString()); + } + break; + } + default: + { + writeMessage("key[%s] val[%s]",key,val); + } + } + return; + } + + // Not parameterized, echo it back as-is + writeMessage(message); + } + + private void writeManyAsync(int size, int count) + { + char letters[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-|{}[]():".toCharArray(); + int lettersLen = letters.length; + char randomText[] = new char[size]; + Random rand = new Random(42); + + for (int n = 0; n < count; n++) + { + // create random text + for (int i = 0; i < size; i++) + { + randomText[i] = letters[rand.nextInt(lettersLen)]; + } + writeMessage("Many [%s]",String.valueOf(randomText)); + } + } + + private void writeMessage(String message) + { + if (this.session == null) + { + LOG.debug("Not connected"); + return; + } + + if (!session.isOpen()) + { + LOG.debug("Not open"); + return; + } + + // Async write + session.getRemote().sendString(message,null); + } + + private void writeMessage(String format, Object... args) + { + writeMessage(String.format(format,args)); + } +} diff --git a/jetty-websocket/jetty-websocket-server/src/test/resources/browser-debug-tool/index.html b/jetty-websocket/jetty-websocket-server/src/test/resources/browser-debug-tool/index.html new file mode 100644 index 00000000000..b7b638d3ba1 --- /dev/null +++ b/jetty-websocket/jetty-websocket-server/src/test/resources/browser-debug-tool/index.html @@ -0,0 +1,58 @@ + + + Jetty WebSocket Browser -> Server Debug Tool + + + + + jetty websocket/browser/javascript -> server debug tool #console +

+
+ + + + + + + + + + + + + + +
+ + + \ No newline at end of file diff --git a/jetty-websocket/jetty-websocket-server/src/test/resources/browser-debug-tool/main.css b/jetty-websocket/jetty-websocket-server/src/test/resources/browser-debug-tool/main.css new file mode 100644 index 00000000000..9eebead468d --- /dev/null +++ b/jetty-websocket/jetty-websocket-server/src/test/resources/browser-debug-tool/main.css @@ -0,0 +1,29 @@ +body { + font-family: sans-serif; +} + +div { + border: 0px solid black; +} + +div#console { + clear: both; + width: 40em; + height: 20em; + overflow: auto; + background-color: #f0f0f0; + padding: 4px; + border: 1px solid black; +} + +div#console .info { + color: black; +} + +div#console .client { + color: blue; +} + +div#console .server { + color: magenta; +} diff --git a/jetty-websocket/jetty-websocket-server/src/test/resources/browser-debug-tool/websocket.js b/jetty-websocket/jetty-websocket-server/src/test/resources/browser-debug-tool/websocket.js new file mode 100644 index 00000000000..66a1ec0ee52 --- /dev/null +++ b/jetty-websocket/jetty-websocket-server/src/test/resources/browser-debug-tool/websocket.js @@ -0,0 +1,141 @@ +if (!window.WebSocket && window.MozWebSocket) { + window.WebSocket = window.MozWebSocket; +} + +if (!window.WebSocket) { + alert("WebSocket not supported by this browser"); +} + +function $() { + return document.getElementById(arguments[0]); +} +function $F() { + return document.getElementById(arguments[0]).value; +} + +function getKeyCode(ev) { + if (window.event) + return window.event.keyCode; + return ev.keyCode; +} + +var wstool = { + connect : function() { + var location = document.location.toString().replace('http://', 'ws://') + .replace('https://', 'wss://'); + + wstool.info("Document URI: " + document.location); + wstool.info("WS URI: " + location); + + this._scount = 0; + + try { + this._ws = new WebSocket(location, "tool"); + this._ws.onopen = this._onopen; + this._ws.onmessage = this._onmessage; + this._ws.onclose = this._onclose; + } catch (exception) { + wstool.info("Connect Error: " + exception); + } + }, + + close : function() { + this._ws.close(); + }, + + _out : function(css, message) { + var console = $('console'); + var spanText = document.createElement('span'); + spanText.className = 'text ' + css; + spanText.innerHTML = message; + var lineBreak = document.createElement('br'); + console.appendChild(spanText); + console.appendChild(lineBreak); + console.scrollTop = console.scrollHeight - console.clientHeight; + }, + + info : function(message) { + wstool._out("info", message); + }, + + infoc : function(message) { + if(message.length > 300) { + wstool._out("client", "[c] [big message: " + message.length + " characters]"); + } else { + wstool._out("client", "[c] " + message); + } + }, + + infos : function(message) { + this._scount++; + if(message.length > 300) { + wstool._out("server", "[s" + this._scount + "] [big message: " + message.length + " characters]"); + } else { + wstool._out("server", "[s" + this._scount + "] " + message); + } + }, + + setState : function(enabled) { + $('connect').disabled = enabled; + $('close').disabled = !enabled; + $('info').disabled = !enabled; + $('time').disabled = !enabled; + $('many').disabled = !enabled; + $('manythreads').disabled = !enabled; + $('hello').disabled = !enabled; + $('there').disabled = !enabled; + $('dump').disabled = !enabled; + $('json').disabled = !enabled; + $('send10k').disabled = !enabled; + $('send100k').disabled = !enabled; + $('send1000k').disabled = !enabled; + $('send10m').disabled = !enabled; + }, + + _onopen : function() { + wstool.setState(true); + wstool.info("Websocket Connected"); + }, + + _send : function(message) { + if (this._ws) { + this._ws.send(message); + wstool.infoc(message); + } + }, + + write : function(text) { + wstool._send(text); + }, + + _onmessage : function(m) { + if (m.data) { + wstool.infos(m.data); + } + }, + + _onclose : function(closeEvent) { + this._ws = null; + wstool.setState(false); + wstool.info("Websocket Closed"); + wstool.info(" .wasClean = " + closeEvent.wasClean); + + var codeMap = {}; + codeMap[1000] = "(NORMAL)"; + codeMap[1001] = "(ENDPOINT_GOING_AWAY)"; + codeMap[1002] = "(PROTOCOL_ERROR)"; + codeMap[1003] = "(UNSUPPORTED_DATA)"; + codeMap[1004] = "(UNUSED/RESERVED)"; + codeMap[1005] = "(INTERNAL/NO_CODE_PRESENT)"; + codeMap[1006] = "(INTERNAL/ABNORMAL_CLOSE)"; + codeMap[1007] = "(BAD_DATA)"; + codeMap[1008] = "(POLICY_VIOLATION)"; + codeMap[1009] = "(MESSAGE_TOO_BIG)"; + codeMap[1010] = "(HANDSHAKE/EXT_FAILURE)"; + codeMap[1011] = "(SERVER/UNEXPECTED_CONDITION)"; + codeMap[1015] = "(INTERNAL/TLS_ERROR)"; + var codeStr = codeMap[closeEvent.code]; + wstool.info(" .code = " + closeEvent.code + " " + codeStr); + wstool.info(" .reason = " + closeEvent.reason); + } +}; diff --git a/jetty-websocket/websocket-core/src/main/java/org/eclipse/jetty/websocket/core/internal/FrameCaptureExtension.java b/jetty-websocket/websocket-core/src/main/java/org/eclipse/jetty/websocket/core/internal/FrameCaptureExtension.java index 6de02f814d8..bccb63333b4 100644 --- a/jetty-websocket/websocket-core/src/main/java/org/eclipse/jetty/websocket/core/internal/FrameCaptureExtension.java +++ b/jetty-websocket/websocket-core/src/main/java/org/eclipse/jetty/websocket/core/internal/FrameCaptureExtension.java @@ -19,6 +19,7 @@ package org.eclipse.jetty.websocket.core.internal; import org.eclipse.jetty.io.ByteBufferPool; +import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.Callback; import org.eclipse.jetty.util.IO; import org.eclipse.jetty.util.StringUtil; @@ -111,12 +112,14 @@ public class FrameCaptureExtension extends AbstractExtension } ByteBuffer buf = getBufferPool().acquire(BUFSIZE, false); + BufferUtil.flipToFill(buf); try { Frame f = Frame.copy(frame); f.setMask(null); // TODO is this needed? generator.generateHeaderBytes(f, buf); + BufferUtil.flipToFlush(buf, 0); channel.write(buf); if (frame.hasPayload()) { diff --git a/jetty-websocket/websocket-servlet/src/main/java/org/eclipse/jetty/websocket/servlet/WebSocketServletFactory.java b/jetty-websocket/websocket-servlet/src/main/java/org/eclipse/jetty/websocket/servlet/WebSocketServletFactory.java index 9d6e7cce07e..b190dbbeef0 100644 --- a/jetty-websocket/websocket-servlet/src/main/java/org/eclipse/jetty/websocket/servlet/WebSocketServletFactory.java +++ b/jetty-websocket/websocket-servlet/src/main/java/org/eclipse/jetty/websocket/servlet/WebSocketServletFactory.java @@ -19,6 +19,7 @@ package org.eclipse.jetty.websocket.servlet; import org.eclipse.jetty.http.pathmap.PathSpec; +import org.eclipse.jetty.websocket.core.WebSocketExtensionRegistry; import java.time.Duration; @@ -40,6 +41,8 @@ public interface WebSocketServletFactory */ void addMapping(PathSpec pathSpec, WebSocketCreator creator); + WebSocketExtensionRegistry getExtensionRegistry(); + Duration getDefaultIdleTimeout(); void setDefaultIdleTimeout(Duration duration);