Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJoakim Erdfelt2015-11-12 00:00:54 +0000
committerJoakim Erdfelt2015-11-12 00:00:54 +0000
commit02b4ac669634dd244a507df4d493f7e2625c2c06 (patch)
tree7f78f9ff377c6cff8163676be9964c01cde813a6
parent13f2f1b64c0b1cfa4e6dd521c16e85607b757ab5 (diff)
downloadorg.eclipse.jetty.project-feature/wsclient-httpclient.tar.gz
org.eclipse.jetty.project-feature/wsclient-httpclient.tar.xz
org.eclipse.jetty.project-feature/wsclient-httpclient.zip
Reworking UpgradeRequest / UpgradeResponse APIsfeature/wsclient-httpclient
+ They are now interfaces (it was impossible to instantiate them with the old API anyway) + The adapters have been broken out (so far, possibly remove in the future) + Reworked WebSocketUpgradeRequest (HttpClient version) to use new UpgradeRequest (websocket api version) interface + Reworked ServletUpgradeRequest and ServletUpgradeResponse to rely on actual HttpServletRequest and HttpServletResponse objects more for their data.
-rw-r--r--jetty-websocket/websocket-api/src/main/java/org/eclipse/jetty/websocket/api/Session.java2
-rw-r--r--jetty-websocket/websocket-api/src/main/java/org/eclipse/jetty/websocket/api/UpgradeRequest.java585
-rw-r--r--jetty-websocket/websocket-api/src/main/java/org/eclipse/jetty/websocket/api/UpgradeResponse.java255
-rw-r--r--jetty-websocket/websocket-api/src/main/java/org/eclipse/jetty/websocket/api/WebSocketConstants.java27
-rw-r--r--jetty-websocket/websocket-client/src/main/java/org/eclipse/jetty/websocket/client/ClientUpgradeRequest.java4
-rw-r--r--jetty-websocket/websocket-client/src/main/java/org/eclipse/jetty/websocket/client/ClientUpgradeResponse.java11
-rw-r--r--jetty-websocket/websocket-client/src/main/java/org/eclipse/jetty/websocket/client/WebSocketClient.java31
-rw-r--r--jetty-websocket/websocket-client/src/main/java/org/eclipse/jetty/websocket/client/WebSocketUpgradeRequest.java404
-rw-r--r--jetty-websocket/websocket-client/src/main/java/org/eclipse/jetty/websocket/client/io/UpgradeListener.java2
-rw-r--r--jetty-websocket/websocket-client/src/test/java/org/eclipse/jetty/websocket/client/JettyTrackingSocket.java2
-rw-r--r--jetty-websocket/websocket-client/src/test/java/org/eclipse/jetty/websocket/client/WebSocketClientTest.java1
-rw-r--r--jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/UpgradeRequestAdapter.java397
-rw-r--r--jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/UpgradeResponseAdapter.java227
-rw-r--r--jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/WebSocketSession.java11
-rw-r--r--jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/RequestHeadersTest.java1
-rw-r--r--jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/WebSocketOverSSLTest.java2
-rw-r--r--jetty-websocket/websocket-servlet/src/main/java/org/eclipse/jetty/websocket/servlet/ServletUpgradeRequest.java364
-rw-r--r--jetty-websocket/websocket-servlet/src/main/java/org/eclipse/jetty/websocket/servlet/ServletUpgradeResponse.java136
18 files changed, 1825 insertions, 637 deletions
diff --git a/jetty-websocket/websocket-api/src/main/java/org/eclipse/jetty/websocket/api/Session.java b/jetty-websocket/websocket-api/src/main/java/org/eclipse/jetty/websocket/api/Session.java
index 3d28ecb290..5e98bc65c1 100644
--- a/jetty-websocket/websocket-api/src/main/java/org/eclipse/jetty/websocket/api/Session.java
+++ b/jetty-websocket/websocket-api/src/main/java/org/eclipse/jetty/websocket/api/Session.java
@@ -22,8 +22,6 @@ import java.io.Closeable;
import java.io.IOException;
import java.net.InetSocketAddress;
-import org.eclipse.jetty.websocket.api.annotations.OnWebSocketClose;
-
/**
* Session represents an active link of communications with a Remote WebSocket Endpoint.
*/
diff --git a/jetty-websocket/websocket-api/src/main/java/org/eclipse/jetty/websocket/api/UpgradeRequest.java b/jetty-websocket/websocket-api/src/main/java/org/eclipse/jetty/websocket/api/UpgradeRequest.java
index 79da9ec20c..38f013ad25 100644
--- a/jetty-websocket/websocket-api/src/main/java/org/eclipse/jetty/websocket/api/UpgradeRequest.java
+++ b/jetty-websocket/websocket-api/src/main/java/org/eclipse/jetty/websocket/api/UpgradeRequest.java
@@ -21,342 +21,307 @@ package org.eclipse.jetty.websocket.api;
import java.net.HttpCookie;
import java.net.URI;
import java.security.Principal;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
import java.util.List;
import java.util.Map;
-import java.util.TreeMap;
import org.eclipse.jetty.websocket.api.extensions.ExtensionConfig;
-import org.eclipse.jetty.websocket.api.util.QuoteUtil;
-public class UpgradeRequest
+/**
+ * The HTTP Upgrade to WebSocket Request
+ */
+public interface UpgradeRequest
{
- private URI requestURI;
- private List<String> subProtocols = new ArrayList<>(1);
- private List<ExtensionConfig> extensions = new ArrayList<>(1);
- private List<HttpCookie> cookies = new ArrayList<>(1);
- private Map<String, List<String>> headers = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
- private Map<String, List<String>> parameters = new HashMap<>(1);
- private Object session;
- private String httpVersion;
- private String method;
- private String host;
- private boolean secure;
-
- protected UpgradeRequest()
- {
- /* anonymous, no requestURI, upgrade request */
- }
-
- public UpgradeRequest(String requestURI)
- {
- this(URI.create(requestURI));
- }
-
- public UpgradeRequest(URI requestURI)
- {
- setRequestURI(requestURI);
- }
-
- public void addExtensions(ExtensionConfig... configs)
- {
- Collections.addAll(extensions, configs);
- }
-
- public void addExtensions(String... configs)
- {
- for (String config : configs)
- {
- extensions.add(ExtensionConfig.parse(config));
- }
- }
-
- public void clearHeaders()
- {
- headers.clear();
- }
-
- public List<HttpCookie> getCookies()
- {
- return cookies;
- }
-
- public List<ExtensionConfig> getExtensions()
- {
- return extensions;
- }
-
- public String getHeader(String name)
- {
- List<String> values = headers.get(name);
- // no value list
- if (values == null)
- {
- return null;
- }
- int size = values.size();
- // empty value list
- if (size <= 0)
- {
- return null;
- }
- // simple return
- if (size == 1)
- {
- return values.get(0);
- }
- // join it with commas
- boolean needsDelim = false;
- StringBuilder ret = new StringBuilder();
- for (String value : values)
- {
- if (needsDelim)
- {
- ret.append(", ");
- }
- QuoteUtil.quoteIfNeeded(ret,value,QuoteUtil.ABNF_REQUIRED_QUOTING);
- needsDelim = true;
- }
- return ret.toString();
- }
-
- public int getHeaderInt(String name)
- {
- List<String> values = headers.get(name);
- // no value list
- if (values == null)
- {
- return -1;
- }
- int size = values.size();
- // empty value list
- if (size <= 0)
- {
- return -1;
- }
- // simple return
- if (size == 1)
- {
- return Integer.parseInt(values.get(0));
- }
- throw new NumberFormatException("Cannot convert multi-value header into int");
- }
-
- public Map<String, List<String>> getHeaders()
- {
- return headers;
- }
-
- public List<String> getHeaders(String name)
- {
- return headers.get(name);
- }
-
- public String getHost()
- {
- return host;
- }
-
- public String getHttpVersion()
- {
- return httpVersion;
- }
-
- public String getMethod()
- {
- return method;
- }
-
- public String getOrigin()
- {
- return getHeader("Origin");
- }
+ /**
+ * Add WebSocket Extension Configuration(s) to Upgrade Request.
+ * <p>
+ * This is merely the list of requested Extensions to use, see {@link UpgradeResponse#getExtensions()} for what was
+ * negotiated
+ *
+ * @param configs the configuration(s) to add
+ */
+ void addExtensions(ExtensionConfig... configs);
+
+ /**
+ * Add WebSocket Extension Configuration(s) to request
+ * <p>
+ * This is merely the list of requested Extensions to use, see {@link UpgradeResponse#getExtensions()} for what was
+ * negotiated
+ *
+ * @param configs the configuration(s) to add
+ */
+ void addExtensions(String... configs);
+
+ /**
+ * Remove all headers from request.
+ * @deprecated (no longer supported, as this can undo the required upgrade request headers)
+ */
+ @Deprecated
+ void clearHeaders();
+
+ /**
+ * Get the list of Cookies on the Upgrade request
+ *
+ * @return the list of Cookies
+ */
+ List<HttpCookie> getCookies();
+
+ /**
+ * Get the list of WebSocket Extension Configurations for this Upgrade Request.
+ * <p>
+ * This is merely the list of requested Extensions to use, see {@link UpgradeResponse#getExtensions()} for what was
+ * negotiated
+ *
+ * @return the list of Extension configurations (in the order they were specified)
+ */
+ List<ExtensionConfig> getExtensions();
+
+ /**
+ * Get a specific Header value from Upgrade Request
+ *
+ * @param name the name of the header
+ * @return the value of the header (null if header does not exist)
+ */
+ String getHeader(String name);
+
+ /**
+ * Get the specific Header value, as an <code>int</code>, from the Upgrade Request.
+ *
+ * @param name the name of the header
+ * @return the value of the header as an <code>int</code> (-1 if header does not exist)
+ * @throws NumberFormatException if unable to parse value as an int.
+ */
+ int getHeaderInt(String name);
+
+ /**
+ * Get the headers as a Map of keys to value lists.
+ *
+ * @return the headers
+ */
+ Map<String, List<String>> getHeaders();
+
+ /**
+ * Get the specific header values (for multi-value headers)
+ *
+ * @param name the header name
+ * @return the value list (null if no header exists)
+ */
+ List<String> getHeaders(String name);
+
+ /**
+ * The host of the Upgrade Request URI
+ * <p>
+ * Equivalent to {@link #getRequestURI()#getHost()}
+ *
+ * @return host of the request URI
+ */
+ String getHost();
+
+ /**
+ * The HTTP version used for this Upgrade Request
+ * <p>
+ * As of <a href="http://tools.ietf.org/html/rfc6455">RFC6455 (December 2011)</a> this is always
+ * <code>HTTP/1.1</code>
+ *
+ * @return the HTTP Version used
+ */
+ String getHttpVersion();
+
+ /**
+ * The HTTP method for this Upgrade Request.
+ * <p>
+ * As of <a href="http://tools.ietf.org/html/rfc6455">RFC6455 (December 2011)</a> this is always <code>GET</code>
+ *
+ * @return the HTTP method used
+ */
+ String getMethod();
+
+ /**
+ * The WebSocket Origin of this Upgrade Request
+ * <p>
+ * See <a href="http://tools.ietf.org/html/rfc6455#section-10.2">RFC6455: Section 10.2</a> for details.
+ * <p>
+ * Equivalent to {@link #getHeader("Origin")}
+ *
+ * @return the Origin header
+ */
+ String getOrigin();
/**
* Returns a map of the query parameters of the request.
- *
+ *
* @return a unmodifiable map of query parameters of the request.
*/
- public Map<String, List<String>> getParameterMap()
- {
- return Collections.unmodifiableMap(parameters);
- }
+ Map<String, List<String>> getParameterMap();
- public String getProtocolVersion()
- {
- String version = getHeader("Sec-WebSocket-Version");
- if (version == null)
- {
- return "13"; // Default
- }
- return version;
- }
+ /**
+ * Get the WebSocket Protocol Version
+ * <p>
+ * As of <a href="http://tools.ietf.org/html/rfc6455#section-11.6">RFC6455</a>, Jetty only supports version
+ * <code>13</code>
+ *
+ * @return the WebSocket protocol version
+ */
+ String getProtocolVersion();
- public String getQueryString()
- {
- return requestURI.getQuery();
- }
+ /**
+ * Get the Query String of the request URI.
+ * <p>
+ * Equivalent to {@link #getRequestURI()#getQueryString()}
+ *
+ * @return the request uri query string
+ */
+ String getQueryString();
- public URI getRequestURI()
- {
- return requestURI;
- }
+ /**
+ * Get the Request URI
+ *
+ * @return the request URI
+ */
+ URI getRequestURI();
/**
* Access the Servlet HTTP Session (if present)
* <p>
* Note: Never present on a Client UpgradeRequest.
- *
+ *
* @return the Servlet HTTPSession on server side UpgradeRequests
*/
- public Object getSession()
- {
- return session;
- }
+ Object getSession();
- public List<String> getSubProtocols()
- {
- return subProtocols;
- }
+ /**
+ * Get the list of offered WebSocket sub-protocols.
+ *
+ * @return the list of offered sub-protocols
+ */
+ List<String> getSubProtocols();
/**
* Get the User Principal for this request.
* <p>
* Only applicable when using UpgradeRequest from server side.
- *
+ *
* @return the user principal
*/
- public Principal getUserPrincipal()
- {
- // Server side should override to implement
- return null;
- }
-
- public boolean hasSubProtocol(String test)
- {
- for (String protocol : subProtocols)
- {
- if (protocol.equalsIgnoreCase(test))
- {
- return true;
- }
- }
- return false;
- }
-
- public boolean isOrigin(String test)
- {
- return test.equalsIgnoreCase(getOrigin());
- }
-
- public boolean isSecure()
- {
- return secure;
- }
-
- public void setCookies(List<HttpCookie> cookies)
- {
- this.cookies.clear();
- if (cookies != null && !cookies.isEmpty())
- {
- this.cookies.addAll(cookies);
- }
- }
-
- public void setExtensions(List<ExtensionConfig> configs)
- {
- this.extensions.clear();
- if (configs != null)
- {
- this.extensions.addAll(configs);
- }
- }
-
- public void setHeader(String name, List<String> values)
- {
- headers.put(name,values);
- }
-
- public void setHeader(String name, String value)
- {
- List<String> values = new ArrayList<>();
- values.add(value);
- setHeader(name,values);
- }
-
- public void setHeaders(Map<String, List<String>> headers)
- {
- clearHeaders();
-
- for (Map.Entry<String, List<String>> entry : headers.entrySet())
- {
- String name = entry.getKey();
- List<String> values = entry.getValue();
- setHeader(name,values);
- }
- }
-
- public void setHttpVersion(String httpVersion)
- {
- this.httpVersion = httpVersion;
- }
-
- public void setMethod(String method)
- {
- this.method = method;
- }
-
- protected void setParameterMap(Map<String, List<String>> parameters)
- {
- this.parameters.clear();
- this.parameters.putAll(parameters);
- }
-
- public void setRequestURI(URI uri)
- {
- this.requestURI = uri;
- String scheme = uri.getScheme();
- if ("ws".equalsIgnoreCase(scheme))
- {
- secure = false;
- }
- else if ("wss".equalsIgnoreCase(scheme))
- {
- secure = true;
- }
- else
- {
- throw new IllegalArgumentException("URI scheme must be 'ws' or 'wss'");
- }
- this.host = this.requestURI.getHost();
- this.parameters.clear();
- }
-
- public void setSession(Object session)
- {
- this.session = session;
- }
-
- public void setSubProtocols(List<String> subProtocols)
- {
- this.subProtocols.clear();
- if (subProtocols != null)
- {
- this.subProtocols.addAll(subProtocols);
- }
- }
-
- /**
- * Set Sub Protocol request list.
- *
- * @param protocols
- * the sub protocols desired
- */
- public void setSubProtocols(String... protocols)
- {
- subProtocols.clear();
- Collections.addAll(subProtocols, protocols);
- }
-}
+ Principal getUserPrincipal();
+
+ /**
+ * Test if a specific sub-protocol is offered
+ *
+ * @param test the sub-protocol to test for
+ * @return true if sub-protocol exists on request
+ */
+ boolean hasSubProtocol(String test);
+
+ /**
+ * Test if supplied Origin is the same as the Request
+ *
+ * @param test the supplied origin
+ * @return true if the supplied origin matches the request origin
+ */
+ boolean isOrigin(String test);
+
+ /**
+ * Test if connection is secure.
+ *
+ * @return true if connection is secure.
+ */
+ boolean isSecure();
+
+ /**
+ * Set the list of Cookies on the request
+ *
+ * @param cookies the cookies to use
+ */
+ void setCookies(List<HttpCookie> cookies);
+
+ /**
+ * Set the list of WebSocket Extension configurations on the request.
+ * @param configs the list of extension configurations
+ */
+ void setExtensions(List<ExtensionConfig> configs);
+
+ /**
+ * Set a specific header with multi-value field
+ * <p>
+ * Overrides any previous value for this named header
+ *
+ * @param name the name of the header
+ * @param values the multi-value field
+ */
+ void setHeader(String name, List<String> values);
+
+ /**
+ * Set a specific header value
+ * <p>
+ * Overrides any previous value for this named header
+ *
+ * @param name the header to set
+ * @param value the value to set it to
+ */
+ void setHeader(String name, String value);
+
+ /**
+ * Sets multiple headers on the request.
+ * <p>
+ * Only sets those headers provided, does not remove
+ * headers that exist on request and are not provided in the
+ * parameter for this method.
+ * <p>
+ * Convenience method vs calling {@link #setHeader(String, List)} multiple times.
+ *
+ * @param headers the headers to set
+ */
+ void setHeaders(Map<String, List<String>> headers);
+
+ /**
+ * Set the HTTP Version to use.
+ * <p>
+ * As of <a href="http://tools.ietf.org/html/rfc6455">RFC6455 (December 2011)</a> this should always be
+ * <code>HTTP/1.1</code>
+ *
+ * @param httpVersion the HTTP version to use.
+ */
+ void setHttpVersion(String httpVersion);
+
+ /**
+ * Set the HTTP method to use.
+ * <p>
+ * As of <a href="http://tools.ietf.org/html/rfc6455">RFC6455 (December 2011)</a> this is always <code>GET</code>
+ *
+ * @param method the HTTP method to use.
+ */
+ void setMethod(String method);
+
+ /**
+ * Set the Request URI to use for this request.
+ * <p>
+ * Must be an absolute URI with scheme <code>'ws'</code> or <code>'wss'</code>
+ *
+ * @param uri the Request URI
+ */
+ void setRequestURI(URI uri);
+
+ /**
+ * Set the Session associated with this request.
+ * <p>
+ * Typically used to associate the Servlet HttpSession object.
+ *
+ * @param session the session object to associate with this request
+ */
+ void setSession(Object session);
+
+ /**
+ * Set the offered WebSocket Sub-Protocol list.
+ *
+ * @param protocols the offered sub-protocol list
+ */
+ void setSubProtocols(List<String> protocols);
+
+ /**
+ * Set the offered WebSocket Sub-Protocol list.
+ *
+ * @param protocols the offered sub-protocol list
+ */
+ void setSubProtocols(String... protocols);
+
+} \ No newline at end of file
diff --git a/jetty-websocket/websocket-api/src/main/java/org/eclipse/jetty/websocket/api/UpgradeResponse.java b/jetty-websocket/websocket-api/src/main/java/org/eclipse/jetty/websocket/api/UpgradeResponse.java
index 91df0d723b..4f0b5932e7 100644
--- a/jetty-websocket/websocket-api/src/main/java/org/eclipse/jetty/websocket/api/UpgradeResponse.java
+++ b/jetty-websocket/websocket-api/src/main/java/org/eclipse/jetty/websocket/api/UpgradeResponse.java
@@ -19,119 +19,92 @@
package org.eclipse.jetty.websocket.api;
import java.io.IOException;
-import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
-import java.util.TreeMap;
import org.eclipse.jetty.websocket.api.extensions.ExtensionConfig;
-import org.eclipse.jetty.websocket.api.util.QuoteUtil;
-public class UpgradeResponse
+/**
+ * The HTTP Upgrade to WebSocket Response
+ */
+public interface UpgradeResponse
{
- public static final String SEC_WEBSOCKET_PROTOCOL = "Sec-WebSocket-Protocol";
- private int statusCode;
- private String statusReason;
- private Map<String, List<String>> headers = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
- private List<ExtensionConfig> extensions = new ArrayList<>();
- private boolean success = false;
-
- public void addHeader(String name, String value)
- {
- String key = name;
- List<String> values = headers.get(key);
- if (values == null)
- {
- values = new ArrayList<>();
- }
- values.add(value);
- headers.put(key,values);
- }
+ /**
+ * Add a header value to the response.
+ *
+ * @param name the header name
+ * @param value the header value
+ */
+ void addHeader(String name, String value);
/**
* Get the accepted WebSocket protocol.
*
* @return the accepted WebSocket protocol.
*/
- public String getAcceptedSubProtocol()
- {
- return getHeader(SEC_WEBSOCKET_PROTOCOL);
- }
+ String getAcceptedSubProtocol();
/**
* Get the list of extensions that should be used for the websocket.
*
* @return the list of negotiated extensions to use.
*/
- public List<ExtensionConfig> getExtensions()
- {
- return extensions;
- }
-
- public String getHeader(String name)
- {
- List<String> values = getHeaders(name);
- // no value list
- if (values == null)
- {
- return null;
- }
- int size = values.size();
- // empty value list
- if (size <= 0)
- {
- return null;
- }
- // simple return
- if (size == 1)
- {
- return values.get(0);
- }
- // join it with commas
- boolean needsDelim = false;
- StringBuilder ret = new StringBuilder();
- for (String value : values)
- {
- if (needsDelim)
- {
- ret.append(", ");
- }
- QuoteUtil.quoteIfNeeded(ret,value,QuoteUtil.ABNF_REQUIRED_QUOTING);
- needsDelim = true;
- }
- return ret.toString();
- }
-
- public Set<String> getHeaderNames()
- {
- return headers.keySet();
- }
-
- public Map<String, List<String>> getHeaders()
- {
- return headers;
- }
-
- public List<String> getHeaders(String name)
- {
- return headers.get(name);
- }
-
- public int getStatusCode()
- {
- return statusCode;
- }
-
- public String getStatusReason()
- {
- return statusReason;
- }
-
- public boolean isSuccess()
- {
- return success;
- }
+ List<ExtensionConfig> getExtensions();
+
+ /**
+ * Get a header value
+ *
+ * @param name the header name
+ * @return the value (null if header doesn't exist)
+ */
+ String getHeader(String name);
+
+ /**
+ * Get the header names
+ *
+ * @return the set of header names
+ */
+ Set<String> getHeaderNames();
+
+ /**
+ * Get the headers map
+ *
+ * @return the map of headers
+ */
+ Map<String, List<String>> getHeaders();
+
+ /**
+ * Get the multi-value header value
+ *
+ * @param name the header name
+ * @return the list of values (null if header doesn't exist)
+ */
+ List<String> getHeaders(String name);
+
+ /**
+ * Get the HTTP Response Status Code
+ *
+ * @return the status code
+ */
+ int getStatusCode();
+
+ /**
+ * Get the HTTP Response Status Reason
+ *
+ * @return the HTTP Response status reason
+ */
+ String getStatusReason();
+
+ /**
+ * Test if upgrade response is successful.
+ * <p>
+ * Merely notes if the response was sent as a WebSocket Upgrade,
+ * or was failed (resulting in no upgrade handshake)
+ *
+ * @return true if upgrade response was generated, false if no upgrade response was generated
+ */
+ boolean isSuccess();
/**
* Issue a forbidden upgrade response.
@@ -142,67 +115,69 @@ public class UpgradeResponse
* Use this when the origin or authentication is invalid.
*
* @param message
- * the short 1 line detail message about the forbidden response
+ * the short 1 line detail message about the forbidden response
* @throws IOException
- * if unable to send the forbidden
+ * if unable to send the forbidden
*/
- public void sendForbidden(String message) throws IOException
- {
- throw new UnsupportedOperationException("Not supported");
- }
+ void sendForbidden(String message) throws IOException;
/**
* Set the accepted WebSocket Protocol.
*
* @param protocol
- * the protocol to list as accepted
+ * the protocol to list as accepted
*/
- public void setAcceptedSubProtocol(String protocol)
- {
- setHeader(SEC_WEBSOCKET_PROTOCOL,protocol);
- }
+ void setAcceptedSubProtocol(String protocol);
/**
* Set the list of extensions that are approved for use with this websocket.
* <p>
* Notes:
* <ul>
- * <li>Per the spec you cannot add extensions that have not been seen in the {@link UpgradeRequest}, just remove entries you don't want to use</li>
- * <li>If this is unused, or a null is passed, then the list negotiation will follow default behavior and use the complete list of extensions that are
+ * <li>Per the spec you cannot add extensions that have not been seen in the {@link UpgradeRequest}, just remove
+ * entries you don't want to use</li>
+ * <li>If this is unused, or a null is passed, then the list negotiation will follow default behavior and use the
+ * complete list of extensions that are
* available in this WebSocket server implementation.</li>
* </ul>
*
* @param extensions
- * the list of extensions to use.
- */
- public void setExtensions(List<ExtensionConfig> extensions)
- {
- this.extensions.clear();
- if (extensions != null)
- {
- this.extensions.addAll(extensions);
- }
- }
-
- public void setHeader(String name, String value)
- {
- List<String> values = new ArrayList<>();
- values.add(value);
- headers.put(name,values);
- }
-
- public void setStatusCode(int statusCode)
- {
- this.statusCode = statusCode;
- }
-
- public void setStatusReason(String statusReason)
- {
- this.statusReason = statusReason;
- }
-
- public void setSuccess(boolean success)
- {
- this.success = success;
- }
-}
+ * the list of extensions to use.
+ */
+ void setExtensions(List<ExtensionConfig> extensions);
+
+ /**
+ * Set a header
+ * <p>
+ * Overrides previous value of header (if set)
+ *
+ * @param name the header name
+ * @param value the header value
+ */
+ void setHeader(String name, String value);
+
+ /**
+ * Set the HTTP Response status code
+ *
+ * @param statusCode the status code
+ */
+ void setStatusCode(int statusCode);
+
+ /**
+ * Set the HTTP Response status reason phrase
+ * <p>
+ * Note, not all implementation of UpgradeResponse can support this feature
+ *
+ * @param statusReason the status reason phrase
+ */
+ void setStatusReason(String statusReason);
+
+ /**
+ * Set the success of the upgrade response.
+ * <p>
+ *
+ * @param success true to indicate a response to the upgrade handshake was sent, false to indicate no upgrade
+ * response was sent
+ */
+ void setSuccess(boolean success);
+} \ No newline at end of file
diff --git a/jetty-websocket/websocket-api/src/main/java/org/eclipse/jetty/websocket/api/WebSocketConstants.java b/jetty-websocket/websocket-api/src/main/java/org/eclipse/jetty/websocket/api/WebSocketConstants.java
new file mode 100644
index 0000000000..da81515bbd
--- /dev/null
+++ b/jetty-websocket/websocket-api/src/main/java/org/eclipse/jetty/websocket/api/WebSocketConstants.java
@@ -0,0 +1,27 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2015 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.api;
+
+public final class WebSocketConstants
+{
+ public static final String SEC_WEBSOCKET_EXTENSIONS = "Sec-WebSocket-Extensions";
+ public static final String SEC_WEBSOCKET_PROTOCOL = "Sec-WebSocket-Protocol";
+ public static final String SEC_WEBSOCKET_VERSION = "Sec-WebSocket-Version";
+ public static final int SPEC_VERSION = 13;
+}
diff --git a/jetty-websocket/websocket-client/src/main/java/org/eclipse/jetty/websocket/client/ClientUpgradeRequest.java b/jetty-websocket/websocket-client/src/main/java/org/eclipse/jetty/websocket/client/ClientUpgradeRequest.java
index d6abcb330b..30c7dea476 100644
--- a/jetty-websocket/websocket-client/src/main/java/org/eclipse/jetty/websocket/client/ClientUpgradeRequest.java
+++ b/jetty-websocket/websocket-client/src/main/java/org/eclipse/jetty/websocket/client/ClientUpgradeRequest.java
@@ -37,13 +37,13 @@ import org.eclipse.jetty.util.B64Code;
import org.eclipse.jetty.util.MultiMap;
import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.UrlEncoded;
-import org.eclipse.jetty.websocket.api.UpgradeRequest;
import org.eclipse.jetty.websocket.api.extensions.ExtensionConfig;
+import org.eclipse.jetty.websocket.common.UpgradeRequestAdapter;
/**
* Allowing a generate from a UpgradeRequest
*/
-public class ClientUpgradeRequest extends UpgradeRequest
+public class ClientUpgradeRequest extends UpgradeRequestAdapter
{
private static final Set<String> FORBIDDEN_HEADERS;
diff --git a/jetty-websocket/websocket-client/src/main/java/org/eclipse/jetty/websocket/client/ClientUpgradeResponse.java b/jetty-websocket/websocket-client/src/main/java/org/eclipse/jetty/websocket/client/ClientUpgradeResponse.java
index 9ed2ab1a72..9103387bdc 100644
--- a/jetty-websocket/websocket-client/src/main/java/org/eclipse/jetty/websocket/client/ClientUpgradeResponse.java
+++ b/jetty-websocket/websocket-client/src/main/java/org/eclipse/jetty/websocket/client/ClientUpgradeResponse.java
@@ -24,10 +24,11 @@ import java.util.List;
import org.eclipse.jetty.client.HttpResponse;
import org.eclipse.jetty.http.HttpField;
import org.eclipse.jetty.http.HttpFields;
-import org.eclipse.jetty.websocket.api.UpgradeResponse;
+import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.websocket.api.extensions.ExtensionConfig;
+import org.eclipse.jetty.websocket.common.UpgradeResponseAdapter;
-public class ClientUpgradeResponse extends UpgradeResponse
+public class ClientUpgradeResponse extends UpgradeResponseAdapter
{
private List<ExtensionConfig> extensions;
@@ -48,8 +49,10 @@ public class ClientUpgradeResponse extends UpgradeResponse
addHeader(field.getName(),field.getValue());
}
- this.extensions = ExtensionConfig.parseEnum(fields.getValues("Sec-WebSocket-Extensions"));
- setAcceptedSubProtocol(fields.get("Sec-WebSocket-Protocol"));
+ HttpField extensionsField = fields.getField(HttpHeader.SEC_WEBSOCKET_EXTENSIONS);
+ if (extensionsField != null)
+ this.extensions = ExtensionConfig.parseList(extensionsField.getValues());
+ setAcceptedSubProtocol(fields.get(HttpHeader.SEC_WEBSOCKET_SUBPROTOCOL));
}
@Override
diff --git a/jetty-websocket/websocket-client/src/main/java/org/eclipse/jetty/websocket/client/WebSocketClient.java b/jetty-websocket/websocket-client/src/main/java/org/eclipse/jetty/websocket/client/WebSocketClient.java
index 24c1302b1d..2a23990b38 100644
--- a/jetty-websocket/websocket-client/src/main/java/org/eclipse/jetty/websocket/client/WebSocketClient.java
+++ b/jetty-websocket/websocket-client/src/main/java/org/eclipse/jetty/websocket/client/WebSocketClient.java
@@ -285,21 +285,35 @@ public class WebSocketClient extends ContainerLifeCycle implements SessionListen
* the websocket uri to connect to
* @param request
* the upgrade request information
- * @param upgradeListener
- * does nothing
* @return the future for the session, available on success of connect
* @throws IOException
* if unable to connect
- * @deprecated {@link UpgradeListener} no longer supported, no alternative available
*/
- @Deprecated
- public Future<Session> connect(Object websocket, URI toUri, ClientUpgradeRequest request, UpgradeListener upgradeListener) throws IOException
+ public Future<Session> connect(Object websocket, URI toUri, ClientUpgradeRequest request) throws IOException
{
- return connect(websocket,toUri,request);
+ return connect(websocket,toUri,request,(UpgradeListener)null);
}
-
- public Future<Session> connect(Object websocket, URI toUri, ClientUpgradeRequest request) throws IOException
+
+ /**
+ * Connect to remote websocket endpoint
+ *
+ * @param websocket
+ * the websocket object
+ * @param toUri
+ * the websocket uri to connect to
+ * @param request
+ * the upgrade request information
+ * @param upgradeListener
+ * the upgrade listener
+ * @return the future for the session, available on success of connect
+ * @throws IOException
+ * if unable to connect
+ */
+ public Future<Session> connect(Object websocket, URI toUri, ClientUpgradeRequest request, UpgradeListener upgradeListener) throws IOException
{
+ /* Note: UpgradeListener is used by javax.websocket.ClientEndpointConfig.Configurator
+ * See: org.eclipse.jetty.websocket.jsr356.JsrUpgradeListener
+ */
if (!isStarted())
{
throw new IllegalStateException(WebSocketClient.class.getSimpleName() + "@" + this.hashCode() + " is not started");
@@ -340,6 +354,7 @@ public class WebSocketClient extends ContainerLifeCycle implements SessionListen
init();
WebSocketUpgradeRequest wsReq = new WebSocketUpgradeRequest(httpClient,request);
+ wsReq.setUpgradeListener(upgradeListener);
return wsReq.sendAsync();
}
diff --git a/jetty-websocket/websocket-client/src/main/java/org/eclipse/jetty/websocket/client/WebSocketUpgradeRequest.java b/jetty-websocket/websocket-client/src/main/java/org/eclipse/jetty/websocket/client/WebSocketUpgradeRequest.java
index c54be66eb7..205e4b2ed1 100644
--- a/jetty-websocket/websocket-client/src/main/java/org/eclipse/jetty/websocket/client/WebSocketUpgradeRequest.java
+++ b/jetty-websocket/websocket-client/src/main/java/org/eclipse/jetty/websocket/client/WebSocketUpgradeRequest.java
@@ -20,8 +20,13 @@ package org.eclipse.jetty.websocket.client;
import java.net.HttpCookie;
import java.net.URI;
+import java.nio.charset.StandardCharsets;
+import java.security.Principal;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ThreadLocalRandom;
@@ -39,20 +44,26 @@ import org.eclipse.jetty.client.api.Result;
import org.eclipse.jetty.client.http.HttpConnectionOverHTTP;
import org.eclipse.jetty.client.http.HttpConnectionUpgrader;
import org.eclipse.jetty.http.HttpField;
+import org.eclipse.jetty.http.HttpFields;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpMethod;
import org.eclipse.jetty.http.HttpStatus;
import org.eclipse.jetty.http.HttpVersion;
import org.eclipse.jetty.io.EndPoint;
import org.eclipse.jetty.util.B64Code;
+import org.eclipse.jetty.util.MultiMap;
import org.eclipse.jetty.util.QuotedStringTokenizer;
import org.eclipse.jetty.util.StringUtil;
+import org.eclipse.jetty.util.UrlEncoded;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.websocket.api.Session;
import org.eclipse.jetty.websocket.api.UpgradeException;
+import org.eclipse.jetty.websocket.api.UpgradeRequest;
+import org.eclipse.jetty.websocket.api.WebSocketConstants;
import org.eclipse.jetty.websocket.api.extensions.ExtensionConfig;
import org.eclipse.jetty.websocket.api.extensions.ExtensionFactory;
+import org.eclipse.jetty.websocket.client.io.UpgradeListener;
import org.eclipse.jetty.websocket.client.io.WebSocketClientConnection;
import org.eclipse.jetty.websocket.common.AcceptHash;
import org.eclipse.jetty.websocket.common.SessionFactory;
@@ -63,12 +74,290 @@ import org.eclipse.jetty.websocket.common.extensions.ExtensionStack;
public class WebSocketUpgradeRequest extends HttpRequest implements CompleteListener, HttpConnectionUpgrader
{
private static final Logger LOG = Log.getLogger(WebSocketUpgradeRequest.class);
+
+ private class ClientUpgradeRequestFacade implements UpgradeRequest
+ {
+ private List<ExtensionConfig> extensions;
+ private List<String> subProtocols;
+ private Object session;
+
+ public ClientUpgradeRequestFacade()
+ {
+ this.extensions = new ArrayList<>();
+ this.subProtocols = new ArrayList<>();
+ }
+
+ public void init(ClientUpgradeRequest request)
+ {
+ this.extensions = new ArrayList<>(request.getExtensions());
+ this.subProtocols = new ArrayList<>(request.getSubProtocols());
+
+ // Copy values from ClientUpgradeRequest into place
+ if (StringUtil.isNotBlank(request.getOrigin()))
+ header(HttpHeader.ORIGIN,request.getOrigin());
+ for (HttpCookie cookie : request.getCookies())
+ {
+ cookie(cookie);
+ }
+ }
+
+ @Override
+ public List<ExtensionConfig> getExtensions()
+ {
+ return extensions;
+ }
+
+ @Override
+ public List<String> getSubProtocols()
+ {
+ return subProtocols;
+ }
+
+ @Override
+ public void addExtensions(ExtensionConfig... configs)
+ {
+ for (ExtensionConfig config : configs)
+ {
+ this.extensions.add(config);
+ }
+ updateExtensionHeader();
+ }
+
+ @Override
+ public void addExtensions(String... configs)
+ {
+ this.extensions.addAll(ExtensionConfig.parseList(configs));
+ updateExtensionHeader();
+ }
+
+ @Override
+ public void clearHeaders()
+ {
+ throw new UnsupportedOperationException("Clearing all headers breaks WebSocket upgrade");
+ }
+
+ @Override
+ public String getHeader(String name)
+ {
+ return getHttpFields().get(name);
+ }
+
+ @Override
+ public int getHeaderInt(String name)
+ {
+ String value = getHttpFields().get(name);
+ if(value == null) {
+ return -1;
+ }
+ return Integer.parseInt(value);
+ }
+
+ @Override
+ public List<String> getHeaders(String name)
+ {
+ return getHttpFields().getValuesList(name);
+ }
+
+ @Override
+ public String getHttpVersion()
+ {
+ return getVersion().asString();
+ }
+
+ @Override
+ public String getOrigin()
+ {
+ return getHttpFields().get(HttpHeader.ORIGIN);
+ }
+
+ @Override
+ public Map<String, List<String>> getParameterMap()
+ {
+ Map<String,List<String>> paramMap = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
+
+ String query = getQueryString();
+ MultiMap<String> multimap = new MultiMap<>();
+ UrlEncoded.decodeTo(query,multimap,StandardCharsets.UTF_8);
+
+ paramMap.putAll(multimap);
+
+ return paramMap;
+ }
+
+ @Override
+ public String getProtocolVersion()
+ {
+ String ver = getHttpFields().get(HttpHeader.SEC_WEBSOCKET_VERSION);
+ if (ver == null)
+ {
+ return Integer.toString(WebSocketConstants.SPEC_VERSION);
+ }
+ return ver;
+ }
+
+ @Override
+ public String getQueryString()
+ {
+ return getURI().getQuery();
+ }
+
+ @Override
+ public URI getRequestURI()
+ {
+ return getURI();
+ }
+
+ @Override
+ public Object getSession()
+ {
+ return this.session;
+ }
+
+ @Override
+ public Principal getUserPrincipal()
+ {
+ // HttpClient doesn't use Principal concepts
+ return null;
+ }
+
+ @Override
+ public boolean hasSubProtocol(String test)
+ {
+ return getSubProtocols().contains(test);
+ }
+
+ @Override
+ public boolean isOrigin(String test)
+ {
+ return test.equalsIgnoreCase(getOrigin());
+ }
+
+ @Override
+ public boolean isSecure()
+ {
+ // TODO: need to obtain information from actual request to know of SSL was used?
+ return "wss".equalsIgnoreCase(getURI().getScheme());
+ }
+
+ @Override
+ public void setCookies(List<HttpCookie> cookies)
+ {
+ for(HttpCookie cookie: cookies)
+ cookie(cookie);
+ }
+
+ @Override
+ public void setExtensions(List<ExtensionConfig> configs)
+ {
+ this.extensions = configs;
+ updateExtensionHeader();
+ }
+
+ private void updateExtensionHeader()
+ {
+ HttpFields headers = getHttpFields();
+ headers.remove(HttpHeader.SEC_WEBSOCKET_EXTENSIONS);
+ for (ExtensionConfig config : extensions)
+ {
+ headers.add(HttpHeader.SEC_WEBSOCKET_EXTENSIONS,config.getParameterizedName());
+ }
+ }
+
+ @Override
+ public void setHeader(String name, List<String> values)
+ {
+ getHttpFields().put(name,values);
+ }
+
+ @Override
+ public void setHeader(String name, String value)
+ {
+ getHttpFields().put(name,value);
+ }
+
+ @Override
+ public void setHeaders(Map<String, List<String>> headers)
+ {
+ for (Map.Entry<String, List<String>> entry : headers.entrySet())
+ {
+ getHttpFields().put(entry.getKey(),entry.getValue());
+ }
+ }
+
+ @Override
+ public void setHttpVersion(String httpVersion)
+ {
+ version(HttpVersion.fromString(httpVersion));
+ }
+
+ @Override
+ public void setMethod(String method)
+ {
+ method(method);
+ }
+
+ @Override
+ public void setRequestURI(URI uri)
+ {
+ throw new UnsupportedOperationException("Cannot reset/change RequestURI");
+ }
+
+ @Override
+ public void setSession(Object session)
+ {
+ this.session = session;
+ }
+
+ @Override
+ public void setSubProtocols(List<String> protocols)
+ {
+ this.subProtocols = protocols;
+ }
+
+ @Override
+ public void setSubProtocols(String... protocols)
+ {
+ this.subProtocols.clear();
+ this.subProtocols.addAll(Arrays.asList(protocols));
+ }
+
+ @Override
+ public List<HttpCookie> getCookies()
+ {
+ return WebSocketUpgradeRequest.this.getCookies();
+ }
+
+ @Override
+ public Map<String, List<String>> getHeaders()
+ {
+ Map<String, List<String>> headersMap = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
+ HttpFields fields = getHttpFields();
+ for(String name: fields.getFieldNamesCollection())
+ {
+ headersMap.put(name,fields.getValuesList(name));
+ }
+ return headersMap;
+ }
+
+ @Override
+ public String getHost()
+ {
+ return WebSocketUpgradeRequest.this.getHost();
+ }
+
+ @Override
+ public String getMethod()
+ {
+ return WebSocketUpgradeRequest.this.getMethod();
+ }
+ }
private final WebSocketClient wsClient;
private final EventDriver localEndpoint;
private final CompletableFuture<Session> fut;
- private List<ExtensionConfig> extensions;
- private List<String> subProtocols;
+ /** WebSocket API UpgradeRequest Facade to HttpClient HttpRequest */
+ private final ClientUpgradeRequestFacade apiRequestFacade;
+ private UpgradeListener upgradeListener;
private static WebSocketClient getManagedWebSocketClient(HttpClient httpClient)
{
@@ -91,49 +380,41 @@ public class WebSocketUpgradeRequest extends HttpRequest implements CompleteList
*/
protected WebSocketUpgradeRequest(HttpClient httpClient, ClientUpgradeRequest request)
{
- this(httpClient, request.getRequestURI(), request.getLocalEndpoint());
-
- // Copy values from ClientUpgradeRequest into place
- this.extensions = new ArrayList<>(request.getExtensions());
- this.subProtocols = new ArrayList<>(request.getSubProtocols());
- if (StringUtil.isNotBlank(request.getOrigin()))
- this.header(HttpHeader.ORIGIN,request.getOrigin());
- for (HttpCookie cookie : request.getCookies())
- {
- this.cookie(cookie);
- }
+ this(httpClient,request.getRequestURI(),request.getLocalEndpoint());
+ apiRequestFacade.init(request);
}
/**
* Initiating a WebSocket Upgrade using HTTP/1.1
*
* @param httpClient the HttpClient that this request uses
- * @param localEndpoint the local endpoint (following Jetty WebSocket Client API rules) to use for incoming WebSocket events
+ * @param localEndpoint the local endpoint (following Jetty WebSocket Client API rules) to use for incoming
+ * WebSocket events
* @param wsURI the WebSocket URI to connect to
*/
public WebSocketUpgradeRequest(HttpClient httpClient, URI wsURI, Object localEndpoint)
{
super(httpClient,new HttpConversation(),wsURI);
+ apiRequestFacade = new ClientUpgradeRequestFacade();
+
if (!wsURI.isAbsolute())
{
throw new IllegalArgumentException("WebSocket URI must be an absolute URI: " + wsURI);
}
-
+
String scheme = wsURI.getScheme();
- if(scheme == null || !(scheme.equalsIgnoreCase("ws") || scheme.equalsIgnoreCase("wss")))
+ if (scheme == null || !(scheme.equalsIgnoreCase("ws") || scheme.equalsIgnoreCase("wss")))
{
throw new IllegalArgumentException("WebSocket URI must use 'ws' or 'wss' scheme: " + wsURI);
}
-
+
// WebSocketClient(HttpClient) -> WebSocketUpgradeRequest(HttpClient, WebSocketClient)
-
+
this.wsClient = getManagedWebSocketClient(httpClient);
this.localEndpoint = this.wsClient.getEventDriverFactory().wrap(localEndpoint);
-
+
this.fut = new CompletableFuture<Session>();
- this.extensions = new ArrayList<>();
- this.subProtocols = new ArrayList<>();
}
private final String genRandomKey()
@@ -148,21 +429,11 @@ public class WebSocketUpgradeRequest extends HttpRequest implements CompleteList
return this.wsClient.getExtensionFactory();
}
- public List<ExtensionConfig> getExtensions()
- {
- return extensions;
- }
-
private SessionFactory getSessionFactory()
{
return this.wsClient.getSessionFactory();
}
- public List<String> getSubProtocols()
- {
- return subProtocols;
- }
-
private void initWebSocketHeaders()
{
method(HttpMethod.GET);
@@ -184,18 +455,18 @@ public class WebSocketUpgradeRequest extends HttpRequest implements CompleteList
header(HttpHeader.CACHE_CONTROL,"no-cache");
// handle "Sec-WebSocket-Extensions"
- if (!getExtensions().isEmpty())
+ if (!apiRequestFacade.getExtensions().isEmpty())
{
- for (ExtensionConfig ext : getExtensions())
+ for (ExtensionConfig ext : apiRequestFacade.getExtensions())
{
header(HttpHeader.SEC_WEBSOCKET_EXTENSIONS,ext.getParameterizedName());
}
}
// handle "Sec-WebSocket-Protocol"
- if (!getSubProtocols().isEmpty())
+ if (!apiRequestFacade.getSubProtocols().isEmpty())
{
- for (String protocol : getSubProtocols())
+ for (String protocol : apiRequestFacade.getSubProtocols())
{
header(HttpHeader.SEC_WEBSOCKET_SUBPROTOCOL,protocol);
}
@@ -209,7 +480,7 @@ public class WebSocketUpgradeRequest extends HttpRequest implements CompleteList
{
LOG.debug("onComplete() - {}",result);
}
-
+
URI requestURI = result.getRequest().getURI();
Response response = result.getResponse();
int responseStatusCode = response.getStatus();
@@ -217,16 +488,15 @@ public class WebSocketUpgradeRequest extends HttpRequest implements CompleteList
if (result.isFailed())
{
- if(result.getFailure()!=null)
- LOG.warn("General Failure", result.getFailure());
- if(result.getRequestFailure()!=null)
- LOG.warn("Request Failure", result.getRequestFailure());
- if(result.getResponseFailure()!=null)
- LOG.warn("Response Failure", result.getResponseFailure());
-
+ if (result.getFailure() != null)
+ LOG.warn("General Failure",result.getFailure());
+ if (result.getRequestFailure() != null)
+ LOG.warn("Request Failure",result.getRequestFailure());
+ if (result.getResponseFailure() != null)
+ LOG.warn("Response Failure",result.getResponseFailure());
+
Throwable failure = result.getFailure();
- if ((failure instanceof java.net.ConnectException) ||
- (failure instanceof UpgradeException))
+ if ((failure instanceof java.net.ConnectException) || (failure instanceof UpgradeException))
{
// handle as-is
handleException(failure);
@@ -237,8 +507,8 @@ public class WebSocketUpgradeRequest extends HttpRequest implements CompleteList
handleException(new UpgradeException(requestURI,responseStatusCode,responseLine,failure));
}
}
-
- if(responseStatusCode != HttpStatus.SWITCHING_PROTOCOLS_101)
+
+ if (responseStatusCode != HttpStatus.SWITCHING_PROTOCOLS_101)
{
// Failed to upgrade (other reason)
handleException(new UpgradeException(requestURI,responseStatusCode,responseLine));
@@ -270,18 +540,6 @@ public class WebSocketUpgradeRequest extends HttpRequest implements CompleteList
return fut;
}
- public WebSocketUpgradeRequest setExtensions(List<ExtensionConfig> extensions)
- {
- this.extensions = extensions;
- return this;
- }
-
- public WebSocketUpgradeRequest setSubProtocols(List<String> subprotocols)
- {
- this.subProtocols = subprotocols;
- return this;
- }
-
@Override
public void upgrade(HttpResponse response, HttpConnectionOverHTTP oldConn)
{
@@ -291,6 +549,11 @@ public class WebSocketUpgradeRequest extends HttpRequest implements CompleteList
throw new HttpResponseException("Not WebSocket Upgrade",response);
}
+ if (upgradeListener != null)
+ {
+ upgradeListener.onHandshakeRequest(apiRequestFacade);
+ }
+
// Check the Accept hash
String reqKey = this.getHeaders().get(HttpHeader.SEC_WEBSOCKET_KEY);
String expectedHash = AcceptHash.hashKey(reqKey);
@@ -304,11 +567,7 @@ public class WebSocketUpgradeRequest extends HttpRequest implements CompleteList
// We can upgrade
EndPoint endp = oldConn.getEndPoint();
- WebSocketClientConnection connection = new WebSocketClientConnection(
- endp,
- wsClient.getExecutor(),
- wsClient.getScheduler(),
- localEndpoint.getPolicy(),
+ WebSocketClientConnection connection = new WebSocketClientConnection(endp,wsClient.getExecutor(),wsClient.getScheduler(),localEndpoint.getPolicy(),
wsClient.getBufferPool());
URI requestURI = this.getURI();
@@ -321,7 +580,7 @@ public class WebSocketUpgradeRequest extends HttpRequest implements CompleteList
ExtensionStack extensionStack = new ExtensionStack(getExtensionFactory());
List<ExtensionConfig> extensions = new ArrayList<>();
HttpField extField = response.getHeaders().getField(HttpHeader.SEC_WEBSOCKET_EXTENSIONS);
- if(extField != null)
+ if (extField != null)
{
String[] extValues = extField.getValues();
if (extValues != null)
@@ -354,7 +613,22 @@ public class WebSocketUpgradeRequest extends HttpRequest implements CompleteList
wsClient.addManaged(session);
+ if (upgradeListener != null)
+ {
+ upgradeListener.onHandshakeResponse(new ClientUpgradeResponse(response));
+ }
+
// Now swap out the connection
endp.upgrade(connection);
}
+
+ public void setUpgradeListener(UpgradeListener upgradeListener)
+ {
+ this.upgradeListener = upgradeListener;
+ }
+
+ private HttpFields getHttpFields()
+ {
+ return super.getHeaders();
+ }
}
diff --git a/jetty-websocket/websocket-client/src/main/java/org/eclipse/jetty/websocket/client/io/UpgradeListener.java b/jetty-websocket/websocket-client/src/main/java/org/eclipse/jetty/websocket/client/io/UpgradeListener.java
index e3dcc8b011..e5d7cb11f9 100644
--- a/jetty-websocket/websocket-client/src/main/java/org/eclipse/jetty/websocket/client/io/UpgradeListener.java
+++ b/jetty-websocket/websocket-client/src/main/java/org/eclipse/jetty/websocket/client/io/UpgradeListener.java
@@ -23,9 +23,7 @@ import org.eclipse.jetty.websocket.api.UpgradeResponse;
/**
* Listener for Handshake/Upgrade events.
- * @deprecated no longer supported, no alternative available
*/
-@Deprecated
public interface UpgradeListener
{
public void onHandshakeRequest(UpgradeRequest request);
diff --git a/jetty-websocket/websocket-client/src/test/java/org/eclipse/jetty/websocket/client/JettyTrackingSocket.java b/jetty-websocket/websocket-client/src/test/java/org/eclipse/jetty/websocket/client/JettyTrackingSocket.java
index 93e49d9c49..51a83a4be7 100644
--- a/jetty-websocket/websocket-client/src/test/java/org/eclipse/jetty/websocket/client/JettyTrackingSocket.java
+++ b/jetty-websocket/websocket-client/src/test/java/org/eclipse/jetty/websocket/client/JettyTrackingSocket.java
@@ -31,9 +31,9 @@ import java.util.concurrent.TimeoutException;
import org.eclipse.jetty.toolchain.test.EventQueue;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
-import org.eclipse.jetty.websocket.api.Session;
import org.eclipse.jetty.websocket.api.UpgradeRequest;
import org.eclipse.jetty.websocket.api.UpgradeResponse;
+import org.eclipse.jetty.websocket.api.Session;
import org.eclipse.jetty.websocket.api.WebSocketAdapter;
import org.junit.Assert;
diff --git a/jetty-websocket/websocket-client/src/test/java/org/eclipse/jetty/websocket/client/WebSocketClientTest.java b/jetty-websocket/websocket-client/src/test/java/org/eclipse/jetty/websocket/client/WebSocketClientTest.java
index 9b1cb0e167..c4a26443b7 100644
--- a/jetty-websocket/websocket-client/src/test/java/org/eclipse/jetty/websocket/client/WebSocketClientTest.java
+++ b/jetty-websocket/websocket-client/src/test/java/org/eclipse/jetty/websocket/client/WebSocketClientTest.java
@@ -36,6 +36,7 @@ import org.eclipse.jetty.websocket.api.RemoteEndpoint;
import org.eclipse.jetty.websocket.api.Session;
import org.eclipse.jetty.websocket.api.UpgradeRequest;
import org.eclipse.jetty.websocket.api.annotations.WebSocket;
+import org.eclipse.jetty.websocket.common.UpgradeRequestAdapter;
import org.eclipse.jetty.websocket.common.WebSocketSession;
import org.eclipse.jetty.websocket.common.frames.TextFrame;
import org.eclipse.jetty.websocket.common.io.FutureWriteCallback;
diff --git a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/UpgradeRequestAdapter.java b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/UpgradeRequestAdapter.java
new file mode 100644
index 0000000000..c9fdea3087
--- /dev/null
+++ b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/UpgradeRequestAdapter.java
@@ -0,0 +1,397 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2015 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.common;
+
+import java.net.HttpCookie;
+import java.net.URI;
+import java.security.Principal;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+
+import org.eclipse.jetty.websocket.api.UpgradeRequest;
+import org.eclipse.jetty.websocket.api.extensions.ExtensionConfig;
+import org.eclipse.jetty.websocket.api.util.QuoteUtil;
+
+public class UpgradeRequestAdapter implements UpgradeRequest
+{
+ private URI requestURI;
+ private List<String> subProtocols = new ArrayList<>(1);
+ private List<ExtensionConfig> extensions = new ArrayList<>(1);
+ private List<HttpCookie> cookies = new ArrayList<>(1);
+ private Map<String, List<String>> headers = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
+ private Map<String, List<String>> parameters = new HashMap<>(1);
+ private Object session;
+ private String httpVersion;
+ private String method;
+ private String host;
+ private boolean secure;
+
+ protected UpgradeRequestAdapter()
+ {
+ /* anonymous, no requestURI, upgrade request */
+ }
+
+ public UpgradeRequestAdapter(String requestURI)
+ {
+ this(URI.create(requestURI));
+ }
+
+ public UpgradeRequestAdapter(URI requestURI)
+ {
+ setRequestURI(requestURI);
+ }
+
+ @Override
+ public void addExtensions(ExtensionConfig... configs)
+ {
+ Collections.addAll(extensions, configs);
+ }
+
+ @Override
+ public void addExtensions(String... configs)
+ {
+ for (String config : configs)
+ {
+ extensions.add(ExtensionConfig.parse(config));
+ }
+ }
+
+ @Override
+ public void clearHeaders()
+ {
+ headers.clear();
+ }
+
+ @Override
+ public List<HttpCookie> getCookies()
+ {
+ return cookies;
+ }
+
+ @Override
+ public List<ExtensionConfig> getExtensions()
+ {
+ return extensions;
+ }
+
+ @Override
+ public String getHeader(String name)
+ {
+ List<String> values = headers.get(name);
+ // no value list
+ if (values == null)
+ {
+ return null;
+ }
+ int size = values.size();
+ // empty value list
+ if (size <= 0)
+ {
+ return null;
+ }
+ // simple return
+ if (size == 1)
+ {
+ return values.get(0);
+ }
+ // join it with commas
+ boolean needsDelim = false;
+ StringBuilder ret = new StringBuilder();
+ for (String value : values)
+ {
+ if (needsDelim)
+ {
+ ret.append(", ");
+ }
+ QuoteUtil.quoteIfNeeded(ret,value,QuoteUtil.ABNF_REQUIRED_QUOTING);
+ needsDelim = true;
+ }
+ return ret.toString();
+ }
+
+ @Override
+ public int getHeaderInt(String name)
+ {
+ List<String> values = headers.get(name);
+ // no value list
+ if (values == null)
+ {
+ return -1;
+ }
+ int size = values.size();
+ // empty value list
+ if (size <= 0)
+ {
+ return -1;
+ }
+ // simple return
+ if (size == 1)
+ {
+ return Integer.parseInt(values.get(0));
+ }
+ throw new NumberFormatException("Cannot convert multi-value header into int");
+ }
+
+ @Override
+ public Map<String, List<String>> getHeaders()
+ {
+ return headers;
+ }
+
+ @Override
+ public List<String> getHeaders(String name)
+ {
+ return headers.get(name);
+ }
+
+ @Override
+ public String getHost()
+ {
+ return host;
+ }
+
+ @Override
+ public String getHttpVersion()
+ {
+ return httpVersion;
+ }
+
+ @Override
+ public String getMethod()
+ {
+ return method;
+ }
+
+ @Override
+ public String getOrigin()
+ {
+ return getHeader("Origin");
+ }
+
+ /**
+ * Returns a map of the query parameters of the request.
+ *
+ * @return a unmodifiable map of query parameters of the request.
+ */
+ @Override
+ public Map<String, List<String>> getParameterMap()
+ {
+ return Collections.unmodifiableMap(parameters);
+ }
+
+ @Override
+ public String getProtocolVersion()
+ {
+ String version = getHeader("Sec-WebSocket-Version");
+ if (version == null)
+ {
+ return "13"; // Default
+ }
+ return version;
+ }
+
+ @Override
+ public String getQueryString()
+ {
+ return requestURI.getQuery();
+ }
+
+ @Override
+ public URI getRequestURI()
+ {
+ return requestURI;
+ }
+
+ /**
+ * Access the Servlet HTTP Session (if present)
+ * <p>
+ * Note: Never present on a Client UpgradeRequest.
+ *
+ * @return the Servlet HTTPSession on server side UpgradeRequests
+ */
+ @Override
+ public Object getSession()
+ {
+ return session;
+ }
+
+ @Override
+ public List<String> getSubProtocols()
+ {
+ return subProtocols;
+ }
+
+ /**
+ * Get the User Principal for this request.
+ * <p>
+ * Only applicable when using UpgradeRequest from server side.
+ *
+ * @return the user principal
+ */
+ @Override
+ public Principal getUserPrincipal()
+ {
+ // Server side should override to implement
+ return null;
+ }
+
+ @Override
+ public boolean hasSubProtocol(String test)
+ {
+ for (String protocol : subProtocols)
+ {
+ if (protocol.equalsIgnoreCase(test))
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public boolean isOrigin(String test)
+ {
+ return test.equalsIgnoreCase(getOrigin());
+ }
+
+ @Override
+ public boolean isSecure()
+ {
+ return secure;
+ }
+
+ @Override
+ public void setCookies(List<HttpCookie> cookies)
+ {
+ this.cookies.clear();
+ if (cookies != null && !cookies.isEmpty())
+ {
+ this.cookies.addAll(cookies);
+ }
+ }
+
+ @Override
+ public void setExtensions(List<ExtensionConfig> configs)
+ {
+ this.extensions.clear();
+ if (configs != null)
+ {
+ this.extensions.addAll(configs);
+ }
+ }
+
+ @Override
+ public void setHeader(String name, List<String> values)
+ {
+ headers.put(name,values);
+ }
+
+ @Override
+ public void setHeader(String name, String value)
+ {
+ List<String> values = new ArrayList<>();
+ values.add(value);
+ setHeader(name,values);
+ }
+
+ @Override
+ public void setHeaders(Map<String, List<String>> headers)
+ {
+ clearHeaders();
+
+ for (Map.Entry<String, List<String>> entry : headers.entrySet())
+ {
+ String name = entry.getKey();
+ List<String> values = entry.getValue();
+ setHeader(name,values);
+ }
+ }
+
+ @Override
+ public void setHttpVersion(String httpVersion)
+ {
+ this.httpVersion = httpVersion;
+ }
+
+ @Override
+ public void setMethod(String method)
+ {
+ this.method = method;
+ }
+
+ protected void setParameterMap(Map<String, List<String>> parameters)
+ {
+ this.parameters.clear();
+ this.parameters.putAll(parameters);
+ }
+
+ @Override
+ public void setRequestURI(URI uri)
+ {
+ this.requestURI = uri;
+ String scheme = uri.getScheme();
+ if ("ws".equalsIgnoreCase(scheme))
+ {
+ secure = false;
+ }
+ else if ("wss".equalsIgnoreCase(scheme))
+ {
+ secure = true;
+ }
+ else
+ {
+ throw new IllegalArgumentException("URI scheme must be 'ws' or 'wss'");
+ }
+ this.host = this.requestURI.getHost();
+ this.parameters.clear();
+ }
+
+ @Override
+ public void setSession(Object session)
+ {
+ this.session = session;
+ }
+
+ @Override
+ public void setSubProtocols(List<String> subProtocols)
+ {
+ this.subProtocols.clear();
+ if (subProtocols != null)
+ {
+ this.subProtocols.addAll(subProtocols);
+ }
+ }
+
+ /**
+ * Set Sub Protocol request list.
+ *
+ * @param protocols
+ * the sub protocols desired
+ */
+ @Override
+ public void setSubProtocols(String... protocols)
+ {
+ subProtocols.clear();
+ Collections.addAll(subProtocols, protocols);
+ }
+}
diff --git a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/UpgradeResponseAdapter.java b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/UpgradeResponseAdapter.java
new file mode 100644
index 0000000000..1043d706ed
--- /dev/null
+++ b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/UpgradeResponseAdapter.java
@@ -0,0 +1,227 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2015 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.common;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+
+import org.eclipse.jetty.websocket.api.UpgradeResponse;
+import org.eclipse.jetty.websocket.api.WebSocketConstants;
+import org.eclipse.jetty.websocket.api.extensions.ExtensionConfig;
+import org.eclipse.jetty.websocket.api.util.QuoteUtil;
+
+public class UpgradeResponseAdapter implements UpgradeResponse
+{
+ public static final String SEC_WEBSOCKET_PROTOCOL = WebSocketConstants.SEC_WEBSOCKET_PROTOCOL;
+ private int statusCode;
+ private String statusReason;
+ private Map<String, List<String>> headers = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
+ private List<ExtensionConfig> extensions = new ArrayList<>();
+ private boolean success = false;
+
+ @Override
+ public void addHeader(String name, String value)
+ {
+ String key = name;
+ List<String> values = headers.get(key);
+ if (values == null)
+ {
+ values = new ArrayList<>();
+ }
+ values.add(value);
+ headers.put(key,values);
+ }
+
+ /**
+ * Get the accepted WebSocket protocol.
+ *
+ * @return the accepted WebSocket protocol.
+ */
+ @Override
+ public String getAcceptedSubProtocol()
+ {
+ return getHeader(SEC_WEBSOCKET_PROTOCOL);
+ }
+
+ /**
+ * Get the list of extensions that should be used for the websocket.
+ *
+ * @return the list of negotiated extensions to use.
+ */
+ @Override
+ public List<ExtensionConfig> getExtensions()
+ {
+ return extensions;
+ }
+
+ @Override
+ public String getHeader(String name)
+ {
+ List<String> values = getHeaders(name);
+ // no value list
+ if (values == null)
+ {
+ return null;
+ }
+ int size = values.size();
+ // empty value list
+ if (size <= 0)
+ {
+ return null;
+ }
+ // simple return
+ if (size == 1)
+ {
+ return values.get(0);
+ }
+ // join it with commas
+ boolean needsDelim = false;
+ StringBuilder ret = new StringBuilder();
+ for (String value : values)
+ {
+ if (needsDelim)
+ {
+ ret.append(", ");
+ }
+ QuoteUtil.quoteIfNeeded(ret,value,QuoteUtil.ABNF_REQUIRED_QUOTING);
+ needsDelim = true;
+ }
+ return ret.toString();
+ }
+
+ @Override
+ public Set<String> getHeaderNames()
+ {
+ return headers.keySet();
+ }
+
+ @Override
+ public Map<String, List<String>> getHeaders()
+ {
+ return headers;
+ }
+
+ @Override
+ public List<String> getHeaders(String name)
+ {
+ return headers.get(name);
+ }
+
+ @Override
+ public int getStatusCode()
+ {
+ return statusCode;
+ }
+
+ @Override
+ public String getStatusReason()
+ {
+ return statusReason;
+ }
+
+ @Override
+ public boolean isSuccess()
+ {
+ return success;
+ }
+
+ /**
+ * Issue a forbidden upgrade response.
+ * <p>
+ * This means that the websocket endpoint was valid, but the conditions to use a WebSocket resulted in a forbidden
+ * access.
+ * <p>
+ * Use this when the origin or authentication is invalid.
+ *
+ * @param message
+ * the short 1 line detail message about the forbidden response
+ * @throws IOException
+ * if unable to send the forbidden
+ */
+ @Override
+ public void sendForbidden(String message) throws IOException
+ {
+ throw new UnsupportedOperationException("Not supported");
+ }
+
+ /**
+ * Set the accepted WebSocket Protocol.
+ *
+ * @param protocol
+ * the protocol to list as accepted
+ */
+ @Override
+ public void setAcceptedSubProtocol(String protocol)
+ {
+ setHeader(SEC_WEBSOCKET_PROTOCOL,protocol);
+ }
+
+ /**
+ * Set the list of extensions that are approved for use with this websocket.
+ * <p>
+ * Notes:
+ * <ul>
+ * <li>Per the spec you cannot add extensions that have not been seen in the {@link UpgradeRequest}, just remove entries you don't want to use</li>
+ * <li>If this is unused, or a null is passed, then the list negotiation will follow default behavior and use the complete list of extensions that are
+ * available in this WebSocket server implementation.</li>
+ * </ul>
+ *
+ * @param extensions
+ * the list of extensions to use.
+ */
+ @Override
+ public void setExtensions(List<ExtensionConfig> extensions)
+ {
+ this.extensions.clear();
+ if (extensions != null)
+ {
+ this.extensions.addAll(extensions);
+ }
+ }
+
+ @Override
+ public void setHeader(String name, String value)
+ {
+ List<String> values = new ArrayList<>();
+ values.add(value);
+ headers.put(name,values);
+ }
+
+ @Override
+ public void setStatusCode(int statusCode)
+ {
+ this.statusCode = statusCode;
+ }
+
+ @Override
+ public void setStatusReason(String statusReason)
+ {
+ this.statusReason = statusReason;
+ }
+
+ @Override
+ public void setSuccess(boolean success)
+ {
+ this.success = success;
+ }
+}
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 5fec26d0b2..c92c58588c 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
@@ -40,12 +40,12 @@ import org.eclipse.jetty.util.thread.ThreadClassLoaderScope;
import org.eclipse.jetty.websocket.api.BatchMode;
import org.eclipse.jetty.websocket.api.CloseException;
import org.eclipse.jetty.websocket.api.CloseStatus;
+import org.eclipse.jetty.websocket.api.UpgradeResponse;
import org.eclipse.jetty.websocket.api.RemoteEndpoint;
import org.eclipse.jetty.websocket.api.Session;
import org.eclipse.jetty.websocket.api.StatusCode;
import org.eclipse.jetty.websocket.api.SuspendToken;
import org.eclipse.jetty.websocket.api.UpgradeRequest;
-import org.eclipse.jetty.websocket.api.UpgradeResponse;
import org.eclipse.jetty.websocket.api.WebSocketBehavior;
import org.eclipse.jetty.websocket.api.WebSocketException;
import org.eclipse.jetty.websocket.api.WebSocketPolicy;
@@ -468,10 +468,6 @@ public class WebSocketSession extends ContainerLifeCycle implements Session, Web
LOG.ignore(t);
}
}
- if(openFuture != null)
- {
- openFuture.complete(this);
- }
break;
}
}
@@ -510,6 +506,11 @@ public class WebSocketSession extends ContainerLifeCycle implements Session, Web
{
LOG.debug("open -> {}",dump());
}
+
+ if(openFuture != null)
+ {
+ openFuture.complete(this);
+ }
}
catch (CloseException ce)
{
diff --git a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/RequestHeadersTest.java b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/RequestHeadersTest.java
index 760a5dc2c5..734a59a424 100644
--- a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/RequestHeadersTest.java
+++ b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/RequestHeadersTest.java
@@ -28,6 +28,7 @@ import java.util.concurrent.TimeUnit;
import org.eclipse.jetty.websocket.api.UpgradeRequest;
import org.eclipse.jetty.websocket.api.UpgradeResponse;
+import org.eclipse.jetty.websocket.common.UpgradeRequestAdapter;
import org.eclipse.jetty.websocket.common.test.BlockheadClient;
import org.eclipse.jetty.websocket.server.helper.EchoSocket;
import org.eclipse.jetty.websocket.servlet.ServletUpgradeRequest;
diff --git a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/WebSocketOverSSLTest.java b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/WebSocketOverSSLTest.java
index 4dcd36f0c4..21a9242317 100644
--- a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/WebSocketOverSSLTest.java
+++ b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/WebSocketOverSSLTest.java
@@ -19,6 +19,8 @@
package org.eclipse.jetty.websocket.server;
import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.notNullValue;
+import static org.junit.Assert.assertThat;
import java.net.URI;
import java.util.concurrent.Future;
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 01c82c20e7..3622b07659 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
@@ -20,6 +20,7 @@ package org.eclipse.jetty.websocket.servlet;
import java.net.HttpCookie;
import java.net.InetSocketAddress;
+import java.net.URI;
import java.net.URISyntaxException;
import java.security.Principal;
import java.security.cert.X509Certificate;
@@ -37,73 +38,124 @@ import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import org.eclipse.jetty.websocket.api.UpgradeRequest;
+import org.eclipse.jetty.websocket.api.WebSocketConstants;
import org.eclipse.jetty.websocket.api.extensions.ExtensionConfig;
-import org.eclipse.jetty.websocket.api.util.WSURI;
/**
* Servlet specific {@link UpgradeRequest} implementation.
*/
-public class ServletUpgradeRequest extends UpgradeRequest
+public class ServletUpgradeRequest implements UpgradeRequest
{
+ private static final String CANNOT_MODIFY_SERVLET_REQUEST = "Cannot modify Servlet Request";
+ private final URI requestURI;
private final UpgradeHttpServletRequest request;
+ private final boolean secure;
+ private List<HttpCookie> cookies;
+ private Map<String, List<String>> parameterMap;
+ private List<String> subprotocols;
public ServletUpgradeRequest(HttpServletRequest httpRequest) throws URISyntaxException
{
- super(WSURI.toWebsocket(httpRequest.getRequestURL(), httpRequest.getQueryString()));
+ URI servletURI = URI.create(httpRequest.getRequestURL().toString());
+ this.secure = httpRequest.isSecure();
+ String scheme = secure ? "wss" : "ws";
+ String authority = servletURI.getAuthority();
+ String path = servletURI.getPath();
+ String query = httpRequest.getQueryString();
+ String fragment = null;
+ this.requestURI = new URI(scheme,authority,path,query,fragment);
this.request = new UpgradeHttpServletRequest(httpRequest);
+ }
- // Parse protocols.
- Enumeration<String> requestProtocols = request.getHeaders("Sec-WebSocket-Protocol");
- if (requestProtocols != null)
- {
- List<String> protocols = new ArrayList<>(2);
- while (requestProtocols.hasMoreElements())
- {
- String candidate = requestProtocols.nextElement();
- Collections.addAll(protocols, parseProtocols(candidate));
- }
- setSubProtocols(protocols);
- }
+ @Override
+ public void addExtensions(ExtensionConfig... configs)
+ {
+ throw new UnsupportedOperationException(CANNOT_MODIFY_SERVLET_REQUEST);
+ }
- // Parse extensions.
- Enumeration<String> e = request.getHeaders("Sec-WebSocket-Extensions");
- setExtensions(ExtensionConfig.parseEnum(e));
+ @Override
+ public void addExtensions(String... configs)
+ {
+ throw new UnsupportedOperationException(CANNOT_MODIFY_SERVLET_REQUEST);
+ }
- // Copy cookies.
- Cookie[] requestCookies = request.getCookies();
- if (requestCookies != null)
+ @Override
+ public void clearHeaders()
+ {
+ throw new UnsupportedOperationException(CANNOT_MODIFY_SERVLET_REQUEST);
+ }
+
+ public void complete()
+ {
+ request.complete();
+ }
+
+ public X509Certificate[] getCertificates()
+ {
+ return (X509Certificate[])request.getAttribute("javax.servlet.request.X509Certificate");
+ }
+
+ @Override
+ public List<HttpCookie> getCookies()
+ {
+ if(cookies == null)
{
- List<HttpCookie> cookies = new ArrayList<>();
- for (Cookie requestCookie : requestCookies)
+ Cookie[] requestCookies = request.getCookies();
+ if (requestCookies != null)
{
- HttpCookie cookie = new HttpCookie(requestCookie.getName(), requestCookie.getValue());
- // No point handling domain/path/expires/secure/httponly on client request cookies
- cookies.add(cookie);
+ cookies = new ArrayList<>();
+ for (Cookie requestCookie : requestCookies)
+ {
+ HttpCookie cookie = new HttpCookie(requestCookie.getName(), requestCookie.getValue());
+ // No point handling domain/path/expires/secure/httponly on client request cookies
+ cookies.add(cookie);
+ }
}
- setCookies(cookies);
}
+
+ return cookies;
+ }
- setHeaders(request.getHeaders());
+ @Override
+ public List<ExtensionConfig> getExtensions()
+ {
+ Enumeration<String> e = request.getHeaders("Sec-WebSocket-Extensions");
+ return ExtensionConfig.parseEnum(e);
+ }
- // Copy parameters.
- Map<String, String[]> requestParams = request.getParameterMap();
- if (requestParams != null)
+ @Override
+ public String getHeader(String name)
+ {
+ return request.getHeader(name);
+ }
+
+ @Override
+ public int getHeaderInt(String name)
+ {
+ String val = request.getHeader(name);
+ if (val == null)
{
- Map<String, List<String>> params = new HashMap<>(requestParams.size());
- for (Map.Entry<String, String[]> entry : requestParams.entrySet())
- params.put(entry.getKey(), Arrays.asList(entry.getValue()));
- setParameterMap(params);
+ return -1;
}
+ return Integer.parseInt(val);
+ }
- setSession(request.getSession(false));
+ @Override
+ public Map<String, List<String>> getHeaders()
+ {
+ return request.getHeaders();
+ }
- setHttpVersion(request.getProtocol());
- setMethod(request.getMethod());
+ @Override
+ public List<String> getHeaders(String name)
+ {
+ return request.getHeaders().get(name);
}
- public X509Certificate[] getCertificates()
+ @Override
+ public String getHost()
{
- return (X509Certificate[])request.getAttribute("javax.servlet.request.X509Certificate");
+ return requestURI.getHost();
}
/**
@@ -119,6 +171,12 @@ public class ServletUpgradeRequest extends UpgradeRequest
return request;
}
+ @Override
+ public String getHttpVersion()
+ {
+ return request.getProtocol();
+ }
+
/**
* Equivalent to {@link HttpServletRequest#getLocalAddr()}
*
@@ -130,6 +188,26 @@ public class ServletUpgradeRequest extends UpgradeRequest
}
/**
+ * Equivalent to {@link HttpServletRequest#getLocale()}
+ *
+ * @return the preferred <code>Locale</code> for the client
+ */
+ public Locale getLocale()
+ {
+ return request.getLocale();
+ }
+
+ /**
+ * Equivalent to {@link HttpServletRequest#getLocales()}
+ *
+ * @return an Enumeration of preferred Locale objects
+ */
+ public Enumeration<Locale> getLocales()
+ {
+ return request.getLocales();
+ }
+
+ /**
* Equivalent to {@link HttpServletRequest#getLocalName()}
*
* @return the local host name
@@ -161,26 +239,34 @@ public class ServletUpgradeRequest extends UpgradeRequest
return new InetSocketAddress(getLocalAddress(), getLocalPort());
}
- /**
- * Equivalent to {@link HttpServletRequest#getLocale()}
- *
- * @return the preferred <code>Locale</code> for the client
- */
- public Locale getLocale()
+ @Override
+ public String getMethod()
{
- return request.getLocale();
+ return request.getMethod();
}
- /**
- * Equivalent to {@link HttpServletRequest#getLocales()}
- *
- * @return an Enumeration of preferred Locale objects
- */
- public Enumeration<Locale> getLocales()
+ @Override
+ public String getOrigin()
{
- return request.getLocales();
+ return getHeader("Origin");
}
+ @Override
+ public Map<String, List<String>> getParameterMap()
+ {
+ if (parameterMap == null)
+ {
+ Map<String, String[]> requestParams = request.getParameterMap();
+ if (requestParams != null)
+ {
+ parameterMap = new HashMap<>(requestParams.size());
+ for (Map.Entry<String, String[]> entry : requestParams.entrySet())
+ parameterMap.put(entry.getKey(),Arrays.asList(entry.getValue()));
+ }
+ }
+ return parameterMap;
+ }
+
/**
* @return the principal
* @deprecated use {@link #getUserPrincipal()} instead
@@ -191,12 +277,21 @@ public class ServletUpgradeRequest extends UpgradeRequest
return getUserPrincipal();
}
- /**
- * Equivalent to {@link HttpServletRequest#getUserPrincipal()}
- */
- public Principal getUserPrincipal()
+ @Override
+ public String getProtocolVersion()
{
- return request.getUserPrincipal();
+ String version = request.getHeader(WebSocketConstants.SEC_WEBSOCKET_VERSION);
+ if(version == null)
+ {
+ return Integer.toString(WebSocketConstants.SPEC_VERSION);
+ }
+ return version;
+ }
+
+ @Override
+ public String getQueryString()
+ {
+ return requestURI.getQuery();
}
/**
@@ -241,11 +336,32 @@ public class ServletUpgradeRequest extends UpgradeRequest
return new InetSocketAddress(getRemoteAddress(), getRemotePort());
}
+ public String getRequestPath()
+ {
+ // Since this can be called from a filter, we need to be smart about determining the target request path.
+ String contextPath = request.getContextPath();
+ String requestPath = request.getRequestURI();
+ if (requestPath.startsWith(contextPath))
+ requestPath = requestPath.substring(contextPath.length());
+ return requestPath;
+ }
+
+ @Override
+ public URI getRequestURI()
+ {
+ return requestURI;
+ }
+
+ public Object getServletAttribute(String name)
+ {
+ return request.getAttribute(name);
+ }
+
public Map<String, Object> getServletAttributes()
{
return request.getAttributes();
}
-
+
public Map<String, List<String>> getServletParameters()
{
return getParameterMap();
@@ -263,29 +379,61 @@ public class ServletUpgradeRequest extends UpgradeRequest
return request.getSession(false);
}
- public void setServletAttribute(String name, Object value)
+ @Override
+ public List<String> getSubProtocols()
{
- request.setAttribute(name, value);
+ if (subprotocols == null)
+ {
+ Enumeration<String> requestProtocols = request.getHeaders("Sec-WebSocket-Protocol");
+ if (requestProtocols != null)
+ {
+ subprotocols = new ArrayList<>(2);
+ while (requestProtocols.hasMoreElements())
+ {
+ String candidate = requestProtocols.nextElement();
+ Collections.addAll(subprotocols,parseProtocols(candidate));
+ }
+ }
+ }
+ return subprotocols;
}
- public Object getServletAttribute(String name)
+ /**
+ * Equivalent to {@link HttpServletRequest#getUserPrincipal()}
+ */
+ public Principal getUserPrincipal()
{
- return request.getAttribute(name);
+ return request.getUserPrincipal();
}
- public boolean isUserInRole(String role)
+ @Override
+ public boolean hasSubProtocol(String test)
{
- return request.isUserInRole(role);
+ for (String protocol : getSubProtocols())
+ {
+ if (protocol.equalsIgnoreCase(test))
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public boolean isOrigin(String test)
+ {
+ return test.equalsIgnoreCase(getOrigin());
}
- public String getRequestPath()
+ @Override
+ public boolean isSecure()
{
- // Since this can be called from a filter, we need to be smart about determining the target request path.
- String contextPath = request.getContextPath();
- String requestPath = request.getRequestURI();
- if (requestPath.startsWith(contextPath))
- requestPath = requestPath.substring(contextPath.length());
- return requestPath;
+ return this.secure;
+ }
+
+ public boolean isUserInRole(String role)
+ {
+ return request.isUserInRole(role);
}
private String[] parseProtocols(String protocol)
@@ -298,8 +446,74 @@ public class ServletUpgradeRequest extends UpgradeRequest
return protocol.split("\\s*,\\s*");
}
- public void complete()
+ @Override
+ public void setCookies(List<HttpCookie> cookies)
{
- request.complete();
+ throw new UnsupportedOperationException(CANNOT_MODIFY_SERVLET_REQUEST);
+ }
+
+ @Override
+ public void setExtensions(List<ExtensionConfig> configs)
+ {
+ throw new UnsupportedOperationException(CANNOT_MODIFY_SERVLET_REQUEST);
+ }
+
+ @Override
+ public void setHeader(String name, List<String> values)
+ {
+ throw new UnsupportedOperationException(CANNOT_MODIFY_SERVLET_REQUEST);
+ }
+
+ @Override
+ public void setHeader(String name, String value)
+ {
+ throw new UnsupportedOperationException(CANNOT_MODIFY_SERVLET_REQUEST);
+ }
+
+ @Override
+ public void setHeaders(Map<String, List<String>> headers)
+ {
+ throw new UnsupportedOperationException(CANNOT_MODIFY_SERVLET_REQUEST);
+ }
+
+ @Override
+ public void setHttpVersion(String httpVersion)
+ {
+ throw new UnsupportedOperationException(CANNOT_MODIFY_SERVLET_REQUEST);
+ }
+
+ @Override
+ public void setMethod(String method)
+ {
+ throw new UnsupportedOperationException(CANNOT_MODIFY_SERVLET_REQUEST);
+ }
+
+ @Override
+ public void setRequestURI(URI uri)
+ {
+ throw new UnsupportedOperationException(CANNOT_MODIFY_SERVLET_REQUEST);
+ }
+
+ public void setServletAttribute(String name, Object value)
+ {
+ request.setAttribute(name, value);
+ }
+
+ @Override
+ public void setSession(Object session)
+ {
+ throw new UnsupportedOperationException(CANNOT_MODIFY_SERVLET_REQUEST);
+ }
+
+ @Override
+ public void setSubProtocols(List<String> subProtocols)
+ {
+ throw new UnsupportedOperationException(CANNOT_MODIFY_SERVLET_REQUEST);
+ }
+
+ @Override
+ public void setSubProtocols(String... protocols)
+ {
+ throw new UnsupportedOperationException(CANNOT_MODIFY_SERVLET_REQUEST);
}
}
diff --git a/jetty-websocket/websocket-servlet/src/main/java/org/eclipse/jetty/websocket/servlet/ServletUpgradeResponse.java b/jetty-websocket/websocket-servlet/src/main/java/org/eclipse/jetty/websocket/servlet/ServletUpgradeResponse.java
index a028284dc0..c2fd478ed6 100644
--- a/jetty-websocket/websocket-servlet/src/main/java/org/eclipse/jetty/websocket/servlet/ServletUpgradeResponse.java
+++ b/jetty-websocket/websocket-servlet/src/main/java/org/eclipse/jetty/websocket/servlet/ServletUpgradeResponse.java
@@ -19,43 +19,110 @@
package org.eclipse.jetty.websocket.servlet;
import java.io.IOException;
+import java.util.ArrayList;
import java.util.List;
import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.websocket.api.UpgradeResponse;
+import org.eclipse.jetty.websocket.api.WebSocketConstants;
import org.eclipse.jetty.websocket.api.extensions.ExtensionConfig;
/**
* Servlet Specific UpgradeResponse implementation.
*/
-public class ServletUpgradeResponse extends UpgradeResponse
+public class ServletUpgradeResponse implements UpgradeResponse
{
private HttpServletResponse response;
private boolean extensionsNegotiated = false;
private boolean subprotocolNegotiated = false;
+ private Map<String, List<String>> headers = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
+ private List<ExtensionConfig> extensions = new ArrayList<>();
+ private boolean success = false;
public ServletUpgradeResponse(HttpServletResponse response)
{
this.response = response;
+
+ for (String name : response.getHeaderNames())
+ {
+ headers.put(name,new ArrayList<String>(response.getHeaders(name)));
+ }
}
@Override
- public int getStatusCode()
+ public void addHeader(String name, String value)
{
- return response.getStatus();
+ this.response.addHeader(name,value);
}
- public void setStatus(int status)
+ private void commitHeaders()
{
- response.setStatus(status);
+ // Transfer all headers to the real HTTP response
+ for (Map.Entry<String, List<String>> entry : getHeaders().entrySet())
+ {
+ for (String value : entry.getValue())
+ {
+ response.addHeader(entry.getKey(),value);
+ }
+ }
+ }
+
+ public void complete()
+ {
+ commitHeaders();
+ response = null;
+ }
+
+ @Override
+ public String getAcceptedSubProtocol()
+ {
+ return getHeader(WebSocketConstants.SEC_WEBSOCKET_PROTOCOL);
+ }
+
+ @Override
+ public List<ExtensionConfig> getExtensions()
+ {
+ return extensions;
+ }
+
+ @Override
+ public String getHeader(String name)
+ {
+ return response.getHeader(name);
+ }
+
+ @Override
+ public Set<String> getHeaderNames()
+ {
+ return getHeaders().keySet();
+ }
+
+ @Override
+ public Map<String, List<String>> getHeaders()
+ {
+ return headers;
+ }
+
+ @Override
+ public List<String> getHeaders(String name)
+ {
+ return getHeaders().get(name);
+ }
+
+ @Override
+ public int getStatusCode()
+ {
+ return response.getStatus();
}
@Override
public String getStatusReason()
{
- throw new UnsupportedOperationException("Server cannot get Status Reason Message");
+ throw new UnsupportedOperationException("Servlet's do not support Status Reason");
}
public boolean isCommitted()
@@ -78,11 +145,17 @@ public class ServletUpgradeResponse extends UpgradeResponse
return subprotocolNegotiated;
}
+ @Override
+ public boolean isSuccess()
+ {
+ return success;
+ }
+
public void sendError(int statusCode, String message) throws IOException
{
setSuccess(false);
commitHeaders();
- response.sendError(statusCode, message);
+ response.sendError(statusCode,message);
response = null;
}
@@ -91,39 +164,56 @@ public class ServletUpgradeResponse extends UpgradeResponse
{
setSuccess(false);
commitHeaders();
- response.sendError(HttpServletResponse.SC_FORBIDDEN, message);
+ response.sendError(HttpServletResponse.SC_FORBIDDEN,message);
response = null;
}
@Override
public void setAcceptedSubProtocol(String protocol)
{
- super.setAcceptedSubProtocol(protocol);
+ response.setHeader(WebSocketConstants.SEC_WEBSOCKET_PROTOCOL,protocol);
subprotocolNegotiated = true;
}
@Override
- public void setExtensions(List<ExtensionConfig> extensions)
+ public void setExtensions(List<ExtensionConfig> configs)
{
- super.setExtensions(extensions);
+ this.extensions.clear();
+ this.extensions.addAll(configs);
+ String value = ExtensionConfig.toHeaderValue(configs);
+ response.setHeader(WebSocketConstants.SEC_WEBSOCKET_EXTENSIONS,value);
extensionsNegotiated = true;
}
- public void complete()
+ @Override
+ public void setHeader(String name, String value)
{
- commitHeaders();
- response = null;
+ List<String> values = new ArrayList<>();
+ values.add(value);
+ headers.put(name,values);
+ response.setHeader(name,value);
}
- private void commitHeaders()
+ public void setStatus(int status)
{
- // Transfer all headers to the real HTTP response
- for (Map.Entry<String, List<String>> entry : getHeaders().entrySet())
- {
- for (String value : entry.getValue())
- {
- response.addHeader(entry.getKey(), value);
- }
- }
+ response.setStatus(status);
+ }
+
+ @Override
+ public void setStatusCode(int statusCode)
+ {
+ response.setStatus(statusCode);
+ }
+
+ @Override
+ public void setStatusReason(String statusReason)
+ {
+ throw new UnsupportedOperationException("Servlet's do not support Status Reason");
+ }
+
+ @Override
+ public void setSuccess(boolean success)
+ {
+ this.success = success;
}
}

Back to the top