Skip to content

Commit d6fe74e

Browse files
committed
Add cherrypicks from latest dev for websocket
1 parent d666d1a commit d6fe74e

File tree

4 files changed

+128
-68
lines changed

4 files changed

+128
-68
lines changed

server/src/main/java/org/red5/net/websocket/server/DefaultWebSocketEndpoint.java

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ public class DefaultWebSocketEndpoint extends Endpoint {
4040
private final boolean isDebug = log.isDebugEnabled(), isTrace = log.isTraceEnabled();
4141

4242
/**
43+
* {@inheritDoc}
44+
*
4345
* TODO: Currently, Tomcat uses an Endpoint instance once - however the java doc of endpoint says: "Each instance
4446
* of a websocket endpoint is guaranteed not to be called by more than one thread at a time per active connection."
4547
* This could mean that after calling onClose(), the instance could be reused for another connection so onOpen()
@@ -58,7 +60,8 @@ public void onOpen(Session session, EndpointConfig config) {
5860
// get ws scope from user props
5961
WebSocketScope scope = (WebSocketScope) userProps.get(WSConstants.WS_SCOPE);
6062
if (isDebug) {
61-
log.debug("onOpen - session: {} props: {} scope: {}", session.getId(), userProps, scope);
63+
log.debug("onOpen - session: {} props: {} scope: {}", session.getId(), scope);
64+
log.trace("onOpen - props: {} ", userProps);
6265
}
6366
// get ws connection from session user props
6467
WebSocketConnection conn = (WebSocketConnection) userProps.get(WSConstants.WS_CONNECTION);
@@ -70,6 +73,7 @@ public void onOpen(Session session, EndpointConfig config) {
7073
session.addMessageHandler(new WholePongHandler(conn));
7174
}
7275

76+
/** {@inheritDoc} */
7377
@Override
7478
public void onClose(Session session, CloseReason closeReason) {
7579
final String sessionId = session.getId();
@@ -79,7 +83,8 @@ public void onClose(Session session, CloseReason closeReason) {
7983
// get ws scope from user props
8084
WebSocketScope scope = (WebSocketScope) userProps.get(WSConstants.WS_SCOPE);
8185
if (isDebug) {
82-
log.debug("onClose - session: {} props: {} scope: {}", session.getId(), userProps, scope);
86+
log.debug("onClose - session: {} scope: {}", session.getId(), scope);
87+
log.trace("onClose - props: {} ", userProps);
8388
}
8489
WebSocketConnection conn = null;
8590
// getting the sessions user properties on a closed connection will throw an exception when it checks state
@@ -106,6 +111,7 @@ public void onClose(Session session, CloseReason closeReason) {
106111
}
107112
}
108113

114+
/** {@inheritDoc} */
109115
@Override
110116
public void onError(Session session, Throwable t) {
111117
log.debug("onError: {}", t.toString(), t);

server/src/main/java/org/red5/net/websocket/server/DefaultWsServerContainer.java

Lines changed: 47 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,11 @@
4040
* Provides a per class loader (i.e. per web application) instance of a ServerContainer. Web application wide defaults may be configured by setting the
4141
* following servlet context initialization parameters to the desired values.
4242
* <ul>
43-
* <li>{@link Constants#BINARY_BUFFER_SIZE_SERVLET_CONTEXT_INIT_PARAM}</li>
44-
* <li>{@link Constants#TEXT_BUFFER_SIZE_SERVLET_CONTEXT_INIT_PARAM}</li>
43+
* <li>{@link org.apache.tomcat.websocket.server.Constants#BINARY_BUFFER_SIZE_SERVLET_CONTEXT_INIT_PARAM}</li>
44+
* <li>{@link org.apache.tomcat.websocket.server.Constants#TEXT_BUFFER_SIZE_SERVLET_CONTEXT_INIT_PARAM}</li>
4545
* </ul>
46+
*
47+
* @author mondain
4648
*/
4749
public class DefaultWsServerContainer extends WsWebSocketContainer implements ServerContainer {
4850

@@ -66,6 +68,11 @@ public class DefaultWsServerContainer extends WsWebSocketContainer implements Se
6668

6769
private volatile CopyOnWriteArraySet<String> registeredEndpointPaths = new CopyOnWriteArraySet<>();
6870

71+
/**
72+
* <p>Constructor for DefaultWsServerContainer.</p>
73+
*
74+
* @param servletContext a {@link jakarta.servlet.ServletContext} object
75+
*/
6976
public DefaultWsServerContainer(ServletContext servletContext) {
7077
log.debug("ctor - context: {}", servletContext);
7178
this.servletContext = servletContext;
@@ -82,13 +89,14 @@ public DefaultWsServerContainer(ServletContext servletContext) {
8289
}
8390

8491
/**
85-
* Published the provided endpoint implementation at the specified path with the specified configuration. {@link #org.apache.tomcat.websocket.server.WsServerContainer(ServletContext)}
86-
* must be called before calling this method.
92+
* {@inheritDoc}
93+
*
94+
* Published the provided endpoint implementation at the specified path with the specified configuration.
8795
*
88-
* @param sec
89-
* The configuration to use when creating endpoint instances
90-
* @throws DeploymentException
91-
* if the endpoint cannot be published as requested
96+
* @see org.apache.tomcat.websocket.server.WsServerContainer#addEndpoint(ServerEndpointConfig)
97+
* @param sec a {@link jakarta.websocket.server.ServerEndpointConfig} object
98+
* @throws jakarta.websocket.DeploymentException if any.
99+
* @throws java.lang.IllegalStateException if the container is not started
92100
*/
93101
@Override
94102
public void addEndpoint(ServerEndpointConfig sec) throws DeploymentException {
@@ -133,10 +141,9 @@ public void addEndpoint(ServerEndpointConfig sec) throws DeploymentException {
133141
}
134142

135143
/**
136-
* Provides the equivalent of {@link #addEndpoint(ServerEndpointConfig)} for publishing plain old java objects (POJOs) that have been annotated as WebSocket endpoints.
144+
* {@inheritDoc}
137145
*
138-
* @param pojo
139-
* The annotated POJO
146+
* Provides the equivalent of {@link #addEndpoint(ServerEndpointConfig)} for publishing plain old java objects (POJOs) that have been annotated as WebSocket endpoints.
140147
*/
141148
@Override
142149
public void addEndpoint(Class<?> pojo) throws DeploymentException {
@@ -168,35 +175,37 @@ boolean areEndpointsRegistered() {
168175
}
169176

170177
/**
178+
* {@inheritDoc}
179+
*
171180
* Until the WebSocket specification provides such a mechanism, this Tomcat proprietary method is provided to enable applications to programmatically
172181
* determine whether or not to upgrade an individual request to WebSocket.
173182
* <p>
174183
* Note: This method is not used by Tomcat but is used directly by third-party code and must not be removed.
175-
*
176-
* @param request
177-
* The request object to be upgraded
178-
* @param response
179-
* The response object to be populated with the result of the upgrade
180-
* @param sec
181-
* The server endpoint to use to process the upgrade request
182-
* @param pathParams
183-
* The path parameters associated with the upgrade request
184-
*
185-
* @throws DeploymentException
186-
* If an error prevents the upgrade from taking place
187-
* @throws IOException
188-
* If an I/O error occurs during the upgrade process
189184
*/
190185
@Override
191186
public void upgradeHttpToWebSocket(Object request, Object response, ServerEndpointConfig sec, Map<String, String> pathParams) throws IOException, DeploymentException {
192-
log.debug("doUpgrade");
187+
log.debug("upgradeHttpToWebSocket");
188+
HttpServletResponse resp = (HttpServletResponse) response;
193189
try {
194-
UpgradeUtil.doUpgrade(this, (HttpServletRequest) request, (HttpServletResponse) response, sec, pathParams);
190+
UpgradeUtil.doUpgrade(this, (HttpServletRequest) request, resp, sec, pathParams);
195191
} catch (ServletException e) {
196192
throw new DeploymentException("Servlet exeception, upgrade failed", e);
197193
}
194+
// did the upgrade fail? check the header set by the upgrade process
195+
if ("true".equals(resp.getHeader("websocket.preinit"))) {
196+
// Upgrade successful
197+
log.debug("Upgrade to WebSocket pre-init successful");
198+
} else {
199+
throw new DeploymentException("WebSocket upgrade failed");
200+
}
198201
}
199202

203+
/**
204+
* <p>findMapping.</p>
205+
*
206+
* @param path a {@link java.lang.String} object
207+
* @return a {@link org.red5.net.websocket.server.WsMappingResult} object
208+
*/
200209
public WsMappingResult findMapping(String path) {
201210
log.debug("findMapping: {}", path);
202211
// Prevent registering additional endpoints once the first attempt has been made to use one
@@ -242,10 +251,16 @@ public WsMappingResult findMapping(String path) {
242251
return new WsMappingResult(sec, pathParams);
243252
}
244253

254+
/**
255+
* <p>Getter for the field <code>registeredEndpointPaths</code>.</p>
256+
*
257+
* @return a {@link java.util.Set} object
258+
*/
245259
public Set<String> getRegisteredEndpointPaths() {
246260
return Collections.unmodifiableSet(registeredEndpointPaths);
247261
}
248262

263+
/** {@inheritDoc} */
249264
@Override
250265
public void backgroundProcess() {
251266
// some comments say 1s others say 10s
@@ -313,6 +328,11 @@ private void unregisterAuthenticatedSession(WsSession wsSession, String httpSess
313328
}
314329
}
315330

331+
/**
332+
* <p>closeAuthenticatedSession.</p>
333+
*
334+
* @param httpSessionId a {@link java.lang.String} object
335+
*/
316336
public void closeAuthenticatedSession(String httpSessionId) {
317337
Set<WsSession> wsSessions = authenticatedSessions.remove(httpSessionId);
318338
if (wsSessions != null && !wsSessions.isEmpty()) {

server/src/main/java/org/red5/net/websocket/server/UpgradeUtil.java

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,11 @@
3232
import org.slf4j.Logger;
3333
import org.slf4j.LoggerFactory;
3434

35+
/**
36+
* <p>UpgradeUtil class.</p>
37+
*
38+
* @author mondain
39+
*/
3540
public class UpgradeUtil {
3641

3742
private static final Logger log = LoggerFactory.getLogger(UpgradeUtil.class);
@@ -40,6 +45,8 @@ public class UpgradeUtil {
4045

4146
private static final byte[] WS_ACCEPT = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11".getBytes(StandardCharsets.ISO_8859_1);
4247

48+
public static boolean wsAllowCompression = true;
49+
4350
private UpgradeUtil() {
4451
// Utility class. Hide default constructor.
4552
}
@@ -68,6 +75,17 @@ public static boolean isWebSocketUpgradeRequest(ServletRequest request, ServletR
6875
return ((request instanceof HttpServletRequest) && (response instanceof HttpServletResponse) && Constants.UPGRADE_HEADER_VALUE.equalsIgnoreCase(((HttpServletRequest) request).getHeader(Constants.UPGRADE_HEADER_NAME)));
6976
}
7077

78+
/**
79+
* <p>doUpgrade.</p>
80+
*
81+
* @param sc a {@link org.red5.net.websocket.server.DefaultWsServerContainer} object
82+
* @param req a {@link jakarta.servlet.http.HttpServletRequest} object
83+
* @param resp a {@link jakarta.servlet.http.HttpServletResponse} object
84+
* @param sec a {@link jakarta.websocket.server.ServerEndpointConfig} object
85+
* @param pathParams a {@link java.util.Map} object
86+
* @throws jakarta.servlet.ServletException if any.
87+
* @throws java.io.IOException if any.
88+
*/
7189
public static void doUpgrade(DefaultWsServerContainer sc, HttpServletRequest req, HttpServletResponse resp, ServerEndpointConfig sec, Map<String, String> pathParams) throws ServletException, IOException {
7290
log.debug("doUpgrade - sc: {} sec: {} params: {}", sc, sec, pathParams);
7391
// Validate the rest of the headers and reject the request if that validation fails
@@ -105,12 +123,12 @@ public static void doUpgrade(DefaultWsServerContainer sc, HttpServletRequest req
105123
}
106124
// Negotiation phase 1. By default this simply filters out the extensions that the server does not support but applications could
107125
// use a custom configurator to do more than this.
108-
List<Extension> installedExtensions = null;
109-
if (sec.getExtensions().size() == 0) {
110-
installedExtensions = Constants.INSTALLED_EXTENSIONS;
111-
} else {
112-
installedExtensions = new ArrayList<>();
126+
List<Extension> installedExtensions = new ArrayList<>();
127+
if (sec.getExtensions().size() > 0) {
113128
installedExtensions.addAll(sec.getExtensions());
129+
}
130+
// Enable permessage-deflate negotiation.
131+
if (wsAllowCompression) {
114132
installedExtensions.addAll(Constants.INSTALLED_EXTENSIONS);
115133
}
116134
List<Extension> negotiatedExtensionsPhase1 = sec.getConfigurator().getNegotiatedExtensions(installedExtensions, extensionsRequested);
@@ -185,7 +203,7 @@ public static void doUpgrade(DefaultWsServerContainer sc, HttpServletRequest req
185203
log.debug("About to upgrade http session: {} qs: {}", wsRequest.getHttpSession(), wsRequest.getQueryString());
186204
WsHttpUpgradeHandler wsHandler = req.upgrade(WsHttpUpgradeHandler.class);
187205
wsHandler.preInit(ep, perSessionServerEndpointConfig, sc, wsRequest, negotiatedExtensionsPhase2, subProtocol, transformation, pathParams, req.isSecure());
188-
log.debug("preinit completed");
206+
resp.setHeader("websocket.preinit", "true");
189207
}
190208

191209
private static List<Transformation> createTransformations(List<Extension> negotiatedExtensions) {

0 commit comments

Comments
 (0)