From 5809f5b8ebd66f1dd4589f7f326a24c10762e24f Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Sat, 21 Oct 2017 23:02:54 +0200 Subject: [PATCH] SubProtocolWebSocketHandler provides protected decorateSession method Issue: SPR-16089 --- .../ConcurrentWebSocketSessionDecorator.java | 10 ++--- .../socket/messaging/SubProtocolHandler.java | 30 +++++++------ .../SubProtocolWebSocketHandler.java | 42 +++++++++++++------ 3 files changed, 48 insertions(+), 34 deletions(-) diff --git a/spring-websocket/src/main/java/org/springframework/web/socket/handler/ConcurrentWebSocketSessionDecorator.java b/spring-websocket/src/main/java/org/springframework/web/socket/handler/ConcurrentWebSocketSessionDecorator.java index 982c41bb057..4b31930a489 100644 --- a/spring-websocket/src/main/java/org/springframework/web/socket/handler/ConcurrentWebSocketSessionDecorator.java +++ b/spring-websocket/src/main/java/org/springframework/web/socket/handler/ConcurrentWebSocketSessionDecorator.java @@ -126,7 +126,7 @@ public class ConcurrentWebSocketSessionDecorator extends WebSocketSessionDecorat if (logger.isTraceEnabled()) { String text = String.format("Another send already in progress: " + "session id '%s':, \"in-progress\" send time %d (ms), buffer size %d bytes", - getId(), getTimeSinceSendStarted(), this.bufferSize.get()); + getId(), getTimeSinceSendStarted(), getBufferSize()); logger.trace(text); } checkSessionLimits(); @@ -166,14 +166,14 @@ public class ConcurrentWebSocketSessionDecorator extends WebSocketSessionDecorat private void checkSessionLimits() { if (!shouldNotSend() && this.closeLock.tryLock()) { try { - if (getTimeSinceSendStarted() > this.sendTimeLimit) { + if (getTimeSinceSendStarted() > getSendTimeLimit()) { String format = "Message send time %d (ms) for session '%s' exceeded the allowed limit %d"; - String reason = String.format(format, getTimeSinceSendStarted(), getId(), this.sendTimeLimit); + String reason = String.format(format, getTimeSinceSendStarted(), getId(), getSendTimeLimit()); limitExceeded(reason); } - else if (this.bufferSize.get() > this.bufferSizeLimit) { + else if (getBufferSize() > getBufferSizeLimit()) { String format = "The send buffer size %d bytes for session '%s' exceeded the allowed limit %d"; - String reason = String.format(format, this.bufferSize.get(), getId(), this.bufferSizeLimit); + String reason = String.format(format, getBufferSize(), getId(), getBufferSizeLimit()); limitExceeded(reason); } } diff --git a/spring-websocket/src/main/java/org/springframework/web/socket/messaging/SubProtocolHandler.java b/spring-websocket/src/main/java/org/springframework/web/socket/messaging/SubProtocolHandler.java index 5c51bf64790..4a007150d0a 100644 --- a/spring-websocket/src/main/java/org/springframework/web/socket/messaging/SubProtocolHandler.java +++ b/spring-websocket/src/main/java/org/springframework/web/socket/messaging/SubProtocolHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2017 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -26,24 +26,23 @@ import org.springframework.web.socket.WebSocketMessage; import org.springframework.web.socket.WebSocketSession; /** - * A contract for handling WebSocket messages as part of a higher level protocol, referred - * to as "sub-protocol" in the WebSocket RFC specification. Handles both + * A contract for handling WebSocket messages as part of a higher level protocol, + * referred to as "sub-protocol" in the WebSocket RFC specification. Handles both * {@link WebSocketMessage}s from a client as well as {@link Message}s to a client. - *

- * Implementations of this interface can be configured on a - * {@link SubProtocolWebSocketHandler} which selects a sub-protocol handler to delegate - * messages to based on the sub-protocol requested by the client through the - * {@code Sec-WebSocket-Protocol} request header. + * + *

Implementations of this interface can be configured on a + * {@link SubProtocolWebSocketHandler} which selects a sub-protocol handler to + * delegate messages to based on the sub-protocol requested by the client through + * the {@code Sec-WebSocket-Protocol} request header. * * @author Andy Wilkinson * @author Rossen Stoyanchev - * * @since 4.0 */ public interface SubProtocolHandler { /** - * Return the list of sub-protocols supported by this handler, never {@code null}. + * Return the list of sub-protocols supported by this handler (never {@code null}). */ List getSupportedProtocols(); @@ -53,12 +52,11 @@ public interface SubProtocolHandler { * @param message the client message * @param outputChannel an output channel to send messages to */ - void handleMessageFromClient(WebSocketSession session, WebSocketMessage message, - MessageChannel outputChannel) throws Exception; + void handleMessageFromClient(WebSocketSession session, WebSocketMessage message, MessageChannel outputChannel) + throws Exception; /** - * Handle the given {@link Message} to the client associated with the given WebSocket - * session. + * Handle the given {@link Message} to the client associated with the given WebSocket session. * @param session the client session * @param message the client message */ @@ -84,7 +82,7 @@ public interface SubProtocolHandler { * @param closeStatus the reason why the session was closed * @param outputChannel a channel */ - void afterSessionEnded(WebSocketSession session, CloseStatus closeStatus, - MessageChannel outputChannel) throws Exception; + void afterSessionEnded(WebSocketSession session, CloseStatus closeStatus, MessageChannel outputChannel) + throws Exception; } diff --git a/spring-websocket/src/main/java/org/springframework/web/socket/messaging/SubProtocolWebSocketHandler.java b/spring-websocket/src/main/java/org/springframework/web/socket/messaging/SubProtocolWebSocketHandler.java index 20e0c3a5d7b..e2d3168033e 100644 --- a/spring-websocket/src/main/java/org/springframework/web/socket/messaging/SubProtocolWebSocketHandler.java +++ b/spring-websocket/src/main/java/org/springframework/web/socket/messaging/SubProtocolWebSocketHandler.java @@ -16,7 +16,6 @@ package org.springframework.web.socket.messaging; -import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.LinkedHashSet; @@ -292,7 +291,7 @@ public class SubProtocolWebSocketHandler return; } this.stats.incrementSessionCount(session); - session = new ConcurrentWebSocketSessionDecorator(session, getSendTimeLimit(), getSendBufferSizeLimit()); + session = decorateSession(session); this.sessions.put(session.getId(), new WebSocketSessionHolder(session)); findProtocolHandler(session).afterSessionStarted(session, this.clientInboundChannel); } @@ -377,6 +376,23 @@ public class SubProtocolWebSocketHandler } + /** + * Decorate the given {@link WebSocketSession}, if desired. + *

The default implementation builds a {@link ConcurrentWebSocketSessionDecorator} + * with the configured {@link #getSendTimeLimit() send-time limit} and + * {@link #getSendBufferSizeLimit() buffer-size limit}. + * @param session the original {@code WebSocketSession} + * @return the decorated {@code WebSocketSession}, or potentially the given session as-is + * @since 4.3.13 + */ + protected WebSocketSession decorateSession(WebSocketSession session) { + return new ConcurrentWebSocketSessionDecorator(session, getSendTimeLimit(), getSendBufferSizeLimit()); + } + + /** + * Find a {@link SubProtocolHandler} for the given session. + * @param session the {@code WebSocketSession} to find a handler for + */ protected final SubProtocolHandler findProtocolHandler(WebSocketSession session) { String protocol = null; try { @@ -432,12 +448,11 @@ public class SubProtocolWebSocketHandler * When a session is connected through a higher-level protocol it has a chance * to use heartbeat management to shut down sessions that are too slow to send * or receive messages. However, after a WebSocketSession is established and - * before the higher level protocol is fully connected there is a possibility - * for sessions to hang. This method checks and closes any sessions that have - * been connected for more than 60 seconds without having received a single - * message. + * before the higher level protocol is fully connected there is a possibility for + * sessions to hang. This method checks and closes any sessions that have been + * connected for more than 60 seconds without having received a single message. */ - private void checkSessions() throws IOException { + private void checkSessions() { long currentTime = System.currentTimeMillis(); if (!isRunning() || (currentTime - this.lastSessionCheckTime < TIME_TO_FIRST_MESSAGE)) { return; @@ -497,12 +512,13 @@ public class SubProtocolWebSocketHandler private final WebSocketSession session; - private final long createTime = System.currentTimeMillis(); + private final long createTime; - private volatile boolean handledMessages; + private volatile boolean hasHandledMessages; - private WebSocketSessionHolder(WebSocketSession session) { + public WebSocketSessionHolder(WebSocketSession session) { this.session = session; + this.createTime = System.currentTimeMillis(); } public WebSocketSession getSession() { @@ -514,17 +530,17 @@ public class SubProtocolWebSocketHandler } public void setHasHandledMessages() { - this.handledMessages = true; + this.hasHandledMessages = true; } public boolean hasHandledMessages() { - return this.handledMessages; + return this.hasHandledMessages; } @Override public String toString() { return "WebSocketSessionHolder[session=" + this.session + ", createTime=" + - this.createTime + ", hasHandledMessages=" + this.handledMessages + "]"; + this.createTime + ", hasHandledMessages=" + this.hasHandledMessages + "]"; } }