diff --git a/jetty-core/jetty-websocket/jetty-websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/Frame.java b/jetty-core/jetty-websocket/jetty-websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/Frame.java index 9feffe21a0b2..2b5d8ac21277 100644 --- a/jetty-core/jetty-websocket/jetty-websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/Frame.java +++ b/jetty-core/jetty-websocket/jetty-websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/Frame.java @@ -18,7 +18,6 @@ import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.StringUtil; -import org.eclipse.jetty.util.TypeUtil; /** * A Base Frame as seen in RFC 6455. Sec 5.2 @@ -412,7 +411,7 @@ public String toString() b.append(((finRsvOp & 0x40) != 0) ? '1' : '0'); b.append(((finRsvOp & 0x20) != 0) ? '1' : '0'); b.append(((finRsvOp & 0x10) != 0) ? '1' : '0'); - b.append(",m=").append(mask == null ? "null" : TypeUtil.toHexString(mask)); + b.append(",m=").append(mask == null ? "null" : StringUtil.toHexString(mask)); b.append(']'); if (payload != null) b.append(BufferUtil.toDetailString(payload)); diff --git a/jetty-core/jetty-websocket/jetty-websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/ServerUpgradeRequest.java b/jetty-core/jetty-websocket/jetty-websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/ServerUpgradeRequest.java index 224072aef7ce..d438b4150f2c 100644 --- a/jetty-core/jetty-websocket/jetty-websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/ServerUpgradeRequest.java +++ b/jetty-core/jetty-websocket/jetty-websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/ServerUpgradeRequest.java @@ -14,131 +14,23 @@ package org.eclipse.jetty.websocket.core.server; import java.util.List; -import java.util.Set; -import org.eclipse.jetty.http.BadMessageException; -import org.eclipse.jetty.http.HttpHeader; import org.eclipse.jetty.server.Request; import org.eclipse.jetty.util.Attributes; import org.eclipse.jetty.websocket.core.ExtensionConfig; import org.eclipse.jetty.websocket.core.WebSocketComponents; -import org.eclipse.jetty.websocket.core.WebSocketConstants; -import org.eclipse.jetty.websocket.core.server.internal.WebSocketNegotiation; -/** - * Upgrade request used for websocket negotiation. - * Provides getters for things like the requested extensions and subprotocols so that the headers don't have to be parsed manually. - */ -public class ServerUpgradeRequest extends Request.Wrapper +public interface ServerUpgradeRequest extends Request { - private final Request request; - private final WebSocketNegotiation negotiation; - private final Attributes attributes = new Attributes.Lazy(); - private boolean upgraded = false; + WebSocketComponents getWebSocketComponents(); - public ServerUpgradeRequest(WebSocketNegotiation negotiation, Request baseRequest) throws BadMessageException - { - super(baseRequest); - this.negotiation = negotiation; - this.request = baseRequest; - } + void upgrade(Attributes attributes); - public WebSocketComponents getWebSocketComponents() - { - return negotiation.getWebSocketComponents(); - } + List getExtensions(); - public void upgrade(Attributes attributes) - { - this.attributes.clearAttributes(); - for (String name : attributes.getAttributeNameSet()) - { - this.attributes.setAttribute(name, attributes.getAttribute(name)); - } - upgraded = true; - } + String getProtocolVersion(); - @Override - public Object removeAttribute(String name) - { - if (upgraded) - return attributes.removeAttribute(name); - return super.removeAttribute(name); - } + List getSubProtocols(); - @Override - public Object setAttribute(String name, Object attribute) - { - if (upgraded) - return attributes.setAttribute(name, attribute); - return super.setAttribute(name, attribute); - } - - @Override - public Object getAttribute(String name) - { - if (upgraded) - return attributes.getAttribute(name); - return super.getAttribute(name); - } - - @Override - public Set getAttributeNameSet() - { - if (upgraded) - return attributes.getAttributeNameSet(); - return super.getAttributeNameSet(); - } - - @Override - public void clearAttributes() - { - if (upgraded) - attributes.clearAttributes(); - else - super.clearAttributes(); - } - - /** - * @return The extensions offered - */ - public List getExtensions() - { - return negotiation.getOfferedExtensions(); - } - - /** - * @return WebSocket protocol version from "Sec-WebSocket-Version" header - */ - public String getProtocolVersion() - { - String version = request.getHeaders().get(HttpHeader.SEC_WEBSOCKET_VERSION.asString()); - if (version == null) - { - return Integer.toString(WebSocketConstants.SPEC_VERSION); - } - return version; - } - - /** - * @return Get WebSocket negotiation offered sub protocols - */ - public List getSubProtocols() - { - return negotiation.getOfferedSubprotocols(); - } - - /** - * @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(subprotocol)) - return true; - } - return false; - } + boolean hasSubProtocol(String subprotocol); } diff --git a/jetty-core/jetty-websocket/jetty-websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/ServerUpgradeResponse.java b/jetty-core/jetty-websocket/jetty-websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/ServerUpgradeResponse.java index 856fc1714ba7..6cb897aab451 100644 --- a/jetty-core/jetty-websocket/jetty-websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/ServerUpgradeResponse.java +++ b/jetty-core/jetty-websocket/jetty-websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/ServerUpgradeResponse.java @@ -13,84 +13,20 @@ package org.eclipse.jetty.websocket.core.server; -import java.util.ArrayList; import java.util.List; -import org.eclipse.jetty.http.HttpFields; import org.eclipse.jetty.server.Response; import org.eclipse.jetty.websocket.core.ExtensionConfig; -import org.eclipse.jetty.websocket.core.server.internal.WebSocketHttpFieldsWrapper; -import org.eclipse.jetty.websocket.core.server.internal.WebSocketNegotiation; -/** - * Upgrade response used for websocket negotiation. - * Allows setting of extensions and subprotocol without using headers directly. - */ -public class ServerUpgradeResponse extends Response.Wrapper +public interface ServerUpgradeResponse extends Response { - private final Response response; - private final WebSocketNegotiation negotiation; - private final HttpFields.Mutable fields; + String getAcceptedSubProtocol(); - public ServerUpgradeResponse(WebSocketNegotiation negotiation, Response baseResponse) - { - super(baseResponse.getRequest(), baseResponse); - this.negotiation = negotiation; - this.response = baseResponse; - this.fields = new WebSocketHttpFieldsWrapper(response.getHeaders(), this, negotiation); - } + void setAcceptedSubProtocol(String protocol); - @Override - public HttpFields.Mutable getHeaders() - { - return fields; - } + List getExtensions(); - public String getAcceptedSubProtocol() - { - return negotiation.getSubprotocol(); - } + void addExtensions(List configs); - public void setAcceptedSubProtocol(String protocol) - { - negotiation.setSubprotocol(protocol); - } - - public List getExtensions() - { - return negotiation.getNegotiatedExtensions(); - } - - public void addExtensions(List configs) - { - ArrayList combinedConfig = new ArrayList<>(); - combinedConfig.addAll(getExtensions()); - combinedConfig.addAll(configs); - setExtensions(combinedConfig); - } - - public void setExtensions(List configs) - { - // This validation is also done later in RFC6455Handshaker but it is better to fail earlier - for (ExtensionConfig config : configs) - { - if (config.getName().startsWith("@")) - continue; - - long matches = negotiation.getOfferedExtensions().stream().filter(e -> e.getName().equals(config.getName())).count(); - if (matches < 1) - throw new IllegalArgumentException("Extension not a requested extension"); - - matches = configs.stream().filter(e -> e.getName().equals(config.getName())).count(); - if (matches > 1) - throw new IllegalArgumentException("Multiple extensions of the same name"); - } - - negotiation.setNegotiatedExtensions(configs); - } - - public String toString() - { - return String.format("UpgradeResponse=%s", response); - } + void setExtensions(List configs); } diff --git a/jetty-core/jetty-websocket/jetty-websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/internal/ServerUpgradeRequestImpl.java b/jetty-core/jetty-websocket/jetty-websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/internal/ServerUpgradeRequestImpl.java new file mode 100644 index 000000000000..4227e11c8ba9 --- /dev/null +++ b/jetty-core/jetty-websocket/jetty-websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/internal/ServerUpgradeRequestImpl.java @@ -0,0 +1,150 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.websocket.core.server.internal; + +import java.util.List; +import java.util.Set; + +import org.eclipse.jetty.http.BadMessageException; +import org.eclipse.jetty.http.HttpHeader; +import org.eclipse.jetty.server.Request; +import org.eclipse.jetty.util.Attributes; +import org.eclipse.jetty.websocket.core.ExtensionConfig; +import org.eclipse.jetty.websocket.core.WebSocketComponents; +import org.eclipse.jetty.websocket.core.WebSocketConstants; +import org.eclipse.jetty.websocket.core.server.ServerUpgradeRequest; + +/** + * Upgrade request used for websocket negotiation. + * Provides getters for things like the requested extensions and subprotocols so that the headers don't have to be parsed manually. + */ +public class ServerUpgradeRequestImpl extends Request.Wrapper implements ServerUpgradeRequest +{ + private final Request request; + private final WebSocketNegotiation negotiation; + private final Attributes attributes = new Attributes.Lazy(); + private boolean upgraded = false; + + public ServerUpgradeRequestImpl(WebSocketNegotiation negotiation, Request baseRequest) throws BadMessageException + { + super(baseRequest); + this.negotiation = negotiation; + this.request = baseRequest; + } + + @Override + public WebSocketComponents getWebSocketComponents() + { + return negotiation.getWebSocketComponents(); + } + + @Override + public void upgrade(Attributes attributes) + { + this.attributes.clearAttributes(); + for (String name : attributes.getAttributeNameSet()) + { + this.attributes.setAttribute(name, attributes.getAttribute(name)); + } + upgraded = true; + } + + @Override + public Object removeAttribute(String name) + { + if (upgraded) + return attributes.removeAttribute(name); + return super.removeAttribute(name); + } + + @Override + public Object setAttribute(String name, Object attribute) + { + if (upgraded) + return attributes.setAttribute(name, attribute); + return super.setAttribute(name, attribute); + } + + @Override + public Object getAttribute(String name) + { + if (upgraded) + return attributes.getAttribute(name); + return super.getAttribute(name); + } + + @Override + public Set getAttributeNameSet() + { + if (upgraded) + return attributes.getAttributeNameSet(); + return super.getAttributeNameSet(); + } + + @Override + public void clearAttributes() + { + if (upgraded) + attributes.clearAttributes(); + else + super.clearAttributes(); + } + + /** + * @return The extensions offered + */ + @Override + public List getExtensions() + { + return negotiation.getOfferedExtensions(); + } + + /** + * @return WebSocket protocol version from "Sec-WebSocket-Version" header + */ + @Override + public String getProtocolVersion() + { + String version = request.getHeaders().get(HttpHeader.SEC_WEBSOCKET_VERSION.asString()); + if (version == null) + { + return Integer.toString(WebSocketConstants.SPEC_VERSION); + } + return version; + } + + /** + * @return Get WebSocket negotiation offered sub protocols + */ + @Override + public List getSubProtocols() + { + return negotiation.getOfferedSubprotocols(); + } + + /** + * @param subprotocol A sub protocol name + * @return True if the sub protocol was offered + */ + @Override + public boolean hasSubProtocol(String subprotocol) + { + for (String protocol : getSubProtocols()) + { + if (protocol.equalsIgnoreCase(subprotocol)) + return true; + } + return false; + } +} diff --git a/jetty-core/jetty-websocket/jetty-websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/internal/ServerUpgradeResponseImpl.java b/jetty-core/jetty-websocket/jetty-websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/internal/ServerUpgradeResponseImpl.java new file mode 100644 index 000000000000..264f6e749743 --- /dev/null +++ b/jetty-core/jetty-websocket/jetty-websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/internal/ServerUpgradeResponseImpl.java @@ -0,0 +1,100 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.websocket.core.server.internal; + +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.jetty.http.HttpFields; +import org.eclipse.jetty.server.Response; +import org.eclipse.jetty.websocket.core.ExtensionConfig; +import org.eclipse.jetty.websocket.core.server.ServerUpgradeResponse; + +/** + * Upgrade response used for websocket negotiation. + * Allows setting of extensions and subprotocol without using headers directly. + */ +public class ServerUpgradeResponseImpl extends Response.Wrapper implements ServerUpgradeResponse +{ + private final Response response; + private final WebSocketNegotiation negotiation; + private final HttpFields.Mutable fields; + + public ServerUpgradeResponseImpl(WebSocketNegotiation negotiation, Response baseResponse) + { + super(baseResponse.getRequest(), baseResponse); + this.negotiation = negotiation; + this.response = baseResponse; + this.fields = new WebSocketHttpFieldsWrapper(response.getHeaders(), this, negotiation); + } + + @Override + public HttpFields.Mutable getHeaders() + { + return fields; + } + + @Override + public String getAcceptedSubProtocol() + { + return negotiation.getSubprotocol(); + } + + @Override + public void setAcceptedSubProtocol(String protocol) + { + negotiation.setSubprotocol(protocol); + } + + @Override + public List getExtensions() + { + return negotiation.getNegotiatedExtensions(); + } + + @Override + public void addExtensions(List configs) + { + ArrayList combinedConfig = new ArrayList<>(); + combinedConfig.addAll(getExtensions()); + combinedConfig.addAll(configs); + setExtensions(combinedConfig); + } + + @Override + public void setExtensions(List configs) + { + // This validation is also done later in RFC6455Handshaker but it is better to fail earlier + for (ExtensionConfig config : configs) + { + if (config.getName().startsWith("@")) + continue; + + long matches = negotiation.getOfferedExtensions().stream().filter(e -> e.getName().equals(config.getName())).count(); + if (matches < 1) + throw new IllegalArgumentException("Extension not a requested extension"); + + matches = configs.stream().filter(e -> e.getName().equals(config.getName())).count(); + if (matches > 1) + throw new IllegalArgumentException("Multiple extensions of the same name"); + } + + negotiation.setNegotiatedExtensions(configs); + } + + public String toString() + { + return String.format("UpgradeResponse=%s", response); + } +} diff --git a/jetty-core/jetty-websocket/jetty-websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/internal/WebSocketNegotiation.java b/jetty-core/jetty-websocket/jetty-websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/internal/WebSocketNegotiation.java index 5a2b516cc998..c11b18bb924b 100644 --- a/jetty-core/jetty-websocket/jetty-websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/internal/WebSocketNegotiation.java +++ b/jetty-core/jetty-websocket/jetty-websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/internal/WebSocketNegotiation.java @@ -45,8 +45,8 @@ public abstract class WebSocketNegotiation public WebSocketNegotiation(Request request, Response response, Callback callback, WebSocketComponents webSocketComponents) { - this.request = new ServerUpgradeRequest(this, request); - this.response = new ServerUpgradeResponse(this, response); + this.request = new ServerUpgradeRequestImpl(this, request); + this.response = new ServerUpgradeResponseImpl(this, response); this.callback = callback; this.components = webSocketComponents; }