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 38bb2f5a1b7..5864f928389 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 @@ -24,6 +24,8 @@ import javax.websocket.server.ServerContainer; 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.javax.server.JavaxWebSocketServerContainerInitializer; @@ -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 = JavaxWebSocketServerContainerInitializer @@ -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-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 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, 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..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 @@ -620,6 +620,16 @@ public class ProxyConnectionFactory extends AbstractConnectionFactory return _remote; } + @Override + public String toString() { + return String.format("%s@%x[remote=%s,local=%s,endpoint=%s]", + getClass().getSimpleName(), + hashCode(), + _remote, + _local, + _endp); + } + @Override public boolean isOpen() { 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 e71555ef7cd..5ecf8e6cbda 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 @@ -69,6 +69,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. @@ -89,6 +98,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","|"); diff --git a/jetty-websocket/jetty-websocket-server/pom.xml b/jetty-websocket/jetty-websocket-server/pom.xml index 8a9be182b40..a63620025b1 100644 --- a/jetty-websocket/jetty-websocket-server/pom.xml +++ b/jetty-websocket/jetty-websocket-server/pom.xml @@ -32,6 +32,12 @@ jetty-server ${project.version} + + org.eclipse.jetty + jetty-jmx + ${project.version} + provided + org.eclipse.jetty.websocket jetty-websocket-api diff --git a/jetty-websocket/jetty-websocket-server/src/main/java/module-info.java b/jetty-websocket/jetty-websocket-server/src/main/java/module-info.java index ada32d8c283..745cff9d63a 100644 --- a/jetty-websocket/jetty-websocket-server/src/main/java/module-info.java +++ b/jetty-websocket/jetty-websocket-server/src/main/java/module-info.java @@ -30,6 +30,7 @@ module org.eclipse.jetty.websocket.jetty.server requires org.eclipse.jetty.util; requires org.eclipse.jetty.http; requires org.eclipse.jetty.server; + requires static org.eclipse.jetty.jmx; requires org.eclipse.jetty.servlet; requires org.eclipse.jetty.webapp; requires org.eclipse.jetty.websocket.jetty.api; diff --git a/jetty-websocket/websocket-core/src/main/java/org/eclipse/jetty/websocket/core/AbstractExtension.java b/jetty-websocket/websocket-core/src/main/java/org/eclipse/jetty/websocket/core/AbstractExtension.java index b034d47abdc..35a71b3209f 100644 --- a/jetty-websocket/websocket-core/src/main/java/org/eclipse/jetty/websocket/core/AbstractExtension.java +++ b/jetty-websocket/websocket-core/src/main/java/org/eclipse/jetty/websocket/core/AbstractExtension.java @@ -18,16 +18,19 @@ package org.eclipse.jetty.websocket.core; +import java.io.IOException; + import org.eclipse.jetty.io.ByteBufferPool; 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.Dumpable; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; import org.eclipse.jetty.websocket.core.internal.WebSocketChannel; @ManagedObject("Abstract Extension") -public abstract class AbstractExtension implements Extension +public abstract class AbstractExtension implements Extension, Dumpable { private final Logger log; private ByteBufferPool bufferPool; @@ -41,6 +44,27 @@ public abstract class AbstractExtension implements Extension 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()); + } + @Override public void init(ExtensionConfig config, ByteBufferPool bufferPool) { diff --git a/jetty-websocket/websocket-core/src/main/java/org/eclipse/jetty/websocket/core/internal/ExtensionStack.java b/jetty-websocket/websocket-core/src/main/java/org/eclipse/jetty/websocket/core/internal/ExtensionStack.java index 30d385490b2..eff21089020 100644 --- a/jetty-websocket/websocket-core/src/main/java/org/eclipse/jetty/websocket/core/internal/ExtensionStack.java +++ b/jetty-websocket/websocket-core/src/main/java/org/eclipse/jetty/websocket/core/internal/ExtensionStack.java @@ -232,6 +232,12 @@ public class ExtensionStack implements IncomingFrames, OutgoingFrames, Dumpable Dumpable.dumpObjects(out, indent, this, extensions == null?Collections.emptyList():extensions); } + @Override + public String dumpSelf() + { + return String.format("%s@%x[size=%d,queueSize=%d]", getClass().getSimpleName(), hashCode(), extensions.size(), getQueueSize()); + } + @Override public String toString() { diff --git a/jetty-websocket/websocket-servlet/src/main/java/org/eclipse/jetty/websocket/servlet/FrameHandlerFactory.java b/jetty-websocket/websocket-servlet/src/main/java/org/eclipse/jetty/websocket/servlet/FrameHandlerFactory.java index 2a781fe88ab..395b06af06c 100644 --- a/jetty-websocket/websocket-servlet/src/main/java/org/eclipse/jetty/websocket/servlet/FrameHandlerFactory.java +++ b/jetty-websocket/websocket-servlet/src/main/java/org/eclipse/jetty/websocket/servlet/FrameHandlerFactory.java @@ -20,7 +20,9 @@ package org.eclipse.jetty.websocket.servlet; import org.eclipse.jetty.websocket.core.FrameHandler; - +/** + * Factory for FrameHandler instances + */ public interface FrameHandlerFactory { /** diff --git a/jetty-websocket/websocket-servlet/src/main/java/org/eclipse/jetty/websocket/servlet/ServletUpgradeRequest.java b/jetty-websocket/websocket-servlet/src/main/java/org/eclipse/jetty/websocket/servlet/ServletUpgradeRequest.java index 81715bb2d8e..b073df0ef18 100644 --- a/jetty-websocket/websocket-servlet/src/main/java/org/eclipse/jetty/websocket/servlet/ServletUpgradeRequest.java +++ b/jetty-websocket/websocket-servlet/src/main/java/org/eclipse/jetty/websocket/servlet/ServletUpgradeRequest.java @@ -18,15 +18,6 @@ package org.eclipse.jetty.websocket.servlet; -import org.eclipse.jetty.http.HttpHeader; -import org.eclipse.jetty.websocket.core.ExtensionConfig; -import org.eclipse.jetty.websocket.core.WebSocketConstants; -import org.eclipse.jetty.websocket.core.server.Negotiation; -import org.eclipse.jetty.websocket.servlet.internal.UpgradeHttpServletRequest; - -import javax.servlet.http.Cookie; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpSession; import java.net.HttpCookie; import java.net.InetSocketAddress; import java.net.SocketAddress; @@ -42,8 +33,19 @@ import java.util.List; import java.util.Locale; import java.util.Map; +import javax.servlet.ServletRequest; +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpSession; + +import org.eclipse.jetty.http.HttpHeader; +import org.eclipse.jetty.websocket.core.ExtensionConfig; +import org.eclipse.jetty.websocket.core.WebSocketConstants; +import org.eclipse.jetty.websocket.core.server.Negotiation; +import org.eclipse.jetty.websocket.servlet.internal.UpgradeHttpServletRequest; + /** - * Servlet specific Upgrade Request implementation. + * Holder of request data for a WebSocket upgrade request. */ public class ServletUpgradeRequest { @@ -62,7 +64,9 @@ public class ServletUpgradeRequest this.queryString = httpRequest.getQueryString(); this.secure = httpRequest.isSecure(); + // TODO why is this URL and not URI? StringBuffer uri = httpRequest.getRequestURL(); + // WHY? if (this.queryString != null) uri.append("?").append(this.queryString); uri.replace(0, uri.indexOf(":"), secure?"wss":"ws"); @@ -70,11 +74,18 @@ public class ServletUpgradeRequest this.request = new UpgradeHttpServletRequest(httpRequest); } + /** + * @return The {@link X509Certificate} instance at request attribute "javax.servlet.request.X509Certificate" or null. + */ public X509Certificate[] getCertificates() { return (X509Certificate[])request.getAttribute("javax.servlet.request.X509Certificate"); } + /** + * @see HttpServletRequest#getCookies() + * @return Request cookies + */ public List getCookies() { if (cookies == null) @@ -95,16 +106,30 @@ public class ServletUpgradeRequest return cookies; } + /** + * @return The extensions offered + * @see Negotiation#getOfferedExtensions() + */ public List getExtensions() { return negotiation.getOfferedExtensions(); } + /** + * @param name Header name + * @return Header value or null + * @see HttpServletRequest#getHeader(String) + */ public String getHeader(String name) { return request.getHeader(name); } + /** + * @param name Header name + * @return Header value as integer or -1 + * @see HttpServletRequest#getHeader(String) + */ public int getHeaderInt(String name) { String val = request.getHeader(name); @@ -115,57 +140,102 @@ public class ServletUpgradeRequest return Integer.parseInt(val); } + /** + * @return Map of headers + * @see UpgradeHttpServletRequest#getHeaders() + */ public Map> getHeadersMap() { return request.getHeaders(); } + /** + * @param name Header name + * @return List of header values or null + * @see UpgradeHttpServletRequest#getHeaders() + */ public List getHeaders(String name) { return request.getHeaders().get(name); } + /** + * @return The requested host + * @see HttpServletRequest#getRequestURL() + */ public String getHost() { + // TODO why is this not HttpServletRequest#getHost ? return requestURI.getHost(); } + /** + * @return Immutable version of {@link HttpServletRequest} + */ public HttpServletRequest getHttpServletRequest() { return request; } + /** + * @return The HTTP protocol version + * @see HttpServletRequest#getProtocol() + */ public String getHttpVersion() { return request.getProtocol(); } + /** + * @return The requested Locale + * @see HttpServletRequest#getLocale() + */ public Locale getLocale() { return request.getLocale(); } + /** + * @return The requested Locales + * @see HttpServletRequest#getLocales() + */ public Enumeration getLocales() { return request.getLocales(); } + /** + * @return The local requested address, which is typically an {@link InetSocketAddress}, but may be another derivation of {@link SocketAddress} + * @see ServletRequest#getLocalAddr() + * @see ServletRequest#getLocalPort() + */ public SocketAddress getLocalSocketAddress() { // TODO: fix when HttpServletRequest can use Unix Socket stuff return new InetSocketAddress(request.getLocalAddr(), request.getLocalPort()); } + /** + * @return The requested method + * @see HttpServletRequest#getMethod() + */ public String getMethod() { return request.getMethod(); } + /** + * @return The origin header value + */ public String getOrigin() { return getHeader("Origin"); } + /** + * @return The request parameter map + * @see ServletRequest#getParameterMap() + */ public Map> getParameterMap() { if (parameterMap == null) @@ -181,6 +251,9 @@ public class ServletUpgradeRequest return parameterMap; } + /** + * @return WebSocket protocol version from "Sec-WebSocket-Version" header + */ public String getProtocolVersion() { String version = request.getHeader(HttpHeader.SEC_WEBSOCKET_VERSION.asString()); @@ -191,19 +264,32 @@ public class ServletUpgradeRequest return version; } + /** + * @return The request query string + * @see HttpServletRequest#getQueryString() + */ public String getQueryString() { return this.queryString; } + /** + * @return The remote request address, which is typically an {@link InetSocketAddress}, but may be another derivation of {@link SocketAddress} + * @see ServletRequest#getRemoteAddr() + * @see ServletRequest#getRemotePort() + */ public SocketAddress getRemoteSocketAddress() { return new InetSocketAddress(request.getRemoteAddr(), request.getRemotePort()); } + /** + * @return The request URI path within the context + */ public String getRequestPath() { // Since this can be called from a filter, we need to be smart about determining the target request path. + // TODO probably better adding servletPath and pathInfo String contextPath = request.getContextPath(); String requestPath = request.getRequestURI(); if (requestPath.startsWith(contextPath)) @@ -211,46 +297,78 @@ public class ServletUpgradeRequest return requestPath; } + /** + * @return The request URI + * @see HttpServletRequest#getRequestURL() + */ public URI getRequestURI() { return requestURI; } + /** + * @param name Attribute name + * @return Attribute value or null + * @see ServletRequest#getAttribute(String) + */ public Object getServletAttribute(String name) { return request.getAttribute(name); } + /** + * @return Request attribute map + * @see UpgradeHttpServletRequest#getAttributes() + */ public Map getServletAttributes() { return request.getAttributes(); } + /** + * @return Request parameters + * @see ServletRequest#getParameterMap() + */ public Map> getServletParameters() { return getParameterMap(); } + /** + * @return The HttpSession, which may be null or invalidated + * @see HttpServletRequest#getSession(boolean) + */ public HttpSession getSession() { return request.getSession(false); } + /** + * @return Get WebSocket negotiation offered sub protocols + */ public List getSubProtocols() { return negotiation.getOfferedSubprotocols(); } + /** + * @return The User's {@link Principal} or null + * @see HttpServletRequest#getUserPrincipal() + */ public Principal getUserPrincipal() { return request.getUserPrincipal(); } - public boolean hasSubProtocol(String test) + /** + * @param subprotocol A sub protocol name + * @return True if the sub protocol was offered + */ + public boolean hasSubProtocol(String subprotocol) { for (String protocol : getSubProtocols()) { - if (protocol.equalsIgnoreCase(test)) + if (protocol.equalsIgnoreCase(subprotocol)) { return true; } @@ -258,16 +376,30 @@ public class ServletUpgradeRequest return false; } + /** + * @return True if the request is secure + * @see ServletRequest#isSecure() + */ public boolean isSecure() { return this.secure; } + /** + * @param role The user role + * @return True if the requests user has the role + * @see HttpServletRequest#isUserInRole(String) + */ public boolean isUserInRole(String role) { return request.isUserInRole(role); } + /** + * @param name Attribute name + * @param value Attribute value to set + * @see ServletRequest#setAttribute(String, Object) + */ public void setServletAttribute(String name, Object value) { request.setAttribute(name, value); diff --git a/jetty-websocket/websocket-servlet/src/main/java/org/eclipse/jetty/websocket/servlet/WebSocketServlet.java b/jetty-websocket/websocket-servlet/src/main/java/org/eclipse/jetty/websocket/servlet/WebSocketServlet.java index 394688689f3..47dfb5c8c33 100644 --- a/jetty-websocket/websocket-servlet/src/main/java/org/eclipse/jetty/websocket/servlet/WebSocketServlet.java +++ b/jetty-websocket/websocket-servlet/src/main/java/org/eclipse/jetty/websocket/servlet/WebSocketServlet.java @@ -33,6 +33,7 @@ import org.eclipse.jetty.servlet.ServletContextHandler; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; import org.eclipse.jetty.websocket.core.FrameHandler; +import org.eclipse.jetty.websocket.core.WebSocketExtensionRegistry; /** * Abstract Servlet used to bridge the Servlet API to the WebSocket API. @@ -173,6 +174,11 @@ public abstract class WebSocketServlet extends HttpServlet private class CustomizedWebSocketServletFactory extends FrameHandler.ConfigurationCustomizer implements WebSocketServletFactory { + public WebSocketExtensionRegistry getExtensionRegistry() + { + return mapping.getExtensionRegistry(); + } + @Override public Duration getDefaultIdleTimeout() { 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 8b839beae6b..04d5528d677 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 @@ -24,6 +24,7 @@ import java.time.Duration; public interface WebSocketServletFactory { + Duration getDefaultIdleTimeout(); void setDefaultIdleTimeout(Duration duration);