From 1f897329f9b8617a84fab10fee5f3ad9269160b3 Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Thu, 29 Aug 2013 15:11:03 -0400 Subject: [PATCH] Add support for Ping and Pong WebSocket messages Issue: SPR-10876 --- .../web/socket/BinaryMessage.java | 8 ++-- .../web/socket/PingMessage.java | 44 +++++++++++++++++++ .../web/socket/PongMessage.java | 44 +++++++++++++++++++ .../web/socket/TextMessage.java | 4 +- .../web/socket/WebSocketMessage.java | 11 +++-- .../adapter/AbstractWebSocketSesssion.java | 15 ++++++- .../socket/adapter/JettyWebSocketSession.java | 12 +++++ .../StandardWebSocketHandlerAdapter.java | 18 ++++++++ .../adapter/StandardWebSocketSession.java | 12 +++++ .../adapter/WebSocketHandlerAdapter.java | 8 ++++ 10 files changed, 165 insertions(+), 11 deletions(-) create mode 100644 spring-websocket/src/main/java/org/springframework/web/socket/PingMessage.java create mode 100644 spring-websocket/src/main/java/org/springframework/web/socket/PongMessage.java diff --git a/spring-websocket/src/main/java/org/springframework/web/socket/BinaryMessage.java b/spring-websocket/src/main/java/org/springframework/web/socket/BinaryMessage.java index 8bcb3a2a3d..d42d3da865 100644 --- a/spring-websocket/src/main/java/org/springframework/web/socket/BinaryMessage.java +++ b/spring-websocket/src/main/java/org/springframework/web/socket/BinaryMessage.java @@ -31,7 +31,7 @@ public final class BinaryMessage extends WebSocketMessage { /** * Create a new {@link BinaryMessage} instance. - * @param payload a non-null payload + * @param payload the non-null payload */ public BinaryMessage(ByteBuffer payload) { this(payload, true); @@ -39,7 +39,7 @@ public final class BinaryMessage extends WebSocketMessage { /** * Create a new {@link BinaryMessage} instance. - * @param payload a non-null payload + * @param payload the non-null payload * @param isLast if the message is the last of a series of partial messages */ public BinaryMessage(ByteBuffer payload, boolean isLast) { @@ -49,7 +49,7 @@ public final class BinaryMessage extends WebSocketMessage { /** * Create a new {@link BinaryMessage} instance. - * @param payload a non-null payload + * @param payload the non-null payload */ public BinaryMessage(byte[] payload) { this(payload, 0, (payload == null ? 0 : payload.length), true); @@ -57,7 +57,7 @@ public final class BinaryMessage extends WebSocketMessage { /** * Create a new {@link BinaryMessage} instance. - * @param payload a non-null payload + * @param payload the non-null payload * @param isLast if the message is the last of a series of partial messages */ public BinaryMessage(byte[] payload, boolean isLast) { diff --git a/spring-websocket/src/main/java/org/springframework/web/socket/PingMessage.java b/spring-websocket/src/main/java/org/springframework/web/socket/PingMessage.java new file mode 100644 index 0000000000..deea0f7dcc --- /dev/null +++ b/spring-websocket/src/main/java/org/springframework/web/socket/PingMessage.java @@ -0,0 +1,44 @@ +/* + * Copyright 2002-2013 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.web.socket; + +import java.nio.ByteBuffer; + +/** + * A WebSocket ping message. + * + * @author Rossen Stoyanchev + * @since 4.0 + */ +public final class PingMessage extends WebSocketMessage { + + + public PingMessage(ByteBuffer payload) { + super(payload); + } + + @Override + protected int getPayloadSize() { + return (getPayload() != null) ? getPayload().remaining() : 0; + } + + @Override + protected String toStringPayload() { + return (getPayload() != null) ? getPayload().toString() : null; + } + +} diff --git a/spring-websocket/src/main/java/org/springframework/web/socket/PongMessage.java b/spring-websocket/src/main/java/org/springframework/web/socket/PongMessage.java new file mode 100644 index 0000000000..42f816c3c5 --- /dev/null +++ b/spring-websocket/src/main/java/org/springframework/web/socket/PongMessage.java @@ -0,0 +1,44 @@ +/* + * Copyright 2002-2013 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.web.socket; + +import java.nio.ByteBuffer; + +/** + * A WebSocket pong message. + * + * @author Rossen Stoyanchev + * @since 4.0 + */ +public final class PongMessage extends WebSocketMessage { + + + public PongMessage(ByteBuffer payload) { + super(payload); + } + + @Override + protected int getPayloadSize() { + return (getPayload() != null) ? getPayload().remaining() : 0; + } + + @Override + protected String toStringPayload() { + return (getPayload() != null) ? getPayload().toString() : null; + } + +} diff --git a/spring-websocket/src/main/java/org/springframework/web/socket/TextMessage.java b/spring-websocket/src/main/java/org/springframework/web/socket/TextMessage.java index ebb10f10db..fb9c4c8412 100644 --- a/spring-websocket/src/main/java/org/springframework/web/socket/TextMessage.java +++ b/spring-websocket/src/main/java/org/springframework/web/socket/TextMessage.java @@ -26,7 +26,7 @@ public final class TextMessage extends WebSocketMessage { /** * Create a new {@link TextMessage} instance. - * @param payload the payload + * @param payload the non-null payload */ public TextMessage(CharSequence payload) { super(payload.toString(), true); @@ -34,7 +34,7 @@ public final class TextMessage extends WebSocketMessage { /** * Create a new {@link TextMessage} instance. - * @param payload the payload + * @param payload the non-null payload * @param isLast whether this the last part of a message received or transmitted in parts */ public TextMessage(CharSequence payload, boolean isLast) { diff --git a/spring-websocket/src/main/java/org/springframework/web/socket/WebSocketMessage.java b/spring-websocket/src/main/java/org/springframework/web/socket/WebSocketMessage.java index 0b934ad7cb..c33697f4ae 100644 --- a/spring-websocket/src/main/java/org/springframework/web/socket/WebSocketMessage.java +++ b/spring-websocket/src/main/java/org/springframework/web/socket/WebSocketMessage.java @@ -24,8 +24,6 @@ import org.springframework.util.ObjectUtils; * * @author Rossen Stoyanchev * @since 4.0 - * @see BinaryMessage - * @see TextMessage */ public abstract class WebSocketMessage { @@ -34,12 +32,19 @@ public abstract class WebSocketMessage { private final boolean last; + /** + * Create a new {@link WebSocketMessage} instance with the given payload. + */ + WebSocketMessage(T payload) { + this(payload, true); + } + /** * Create a new {@link WebSocketMessage} instance with the given payload. * @param payload a non-null payload */ WebSocketMessage(T payload, boolean isLast) { - Assert.notNull(payload, "Payload must not be null"); + Assert.notNull(payload, "payload is required"); this.payload = payload; this.last = isLast; } diff --git a/spring-websocket/src/main/java/org/springframework/web/socket/adapter/AbstractWebSocketSesssion.java b/spring-websocket/src/main/java/org/springframework/web/socket/adapter/AbstractWebSocketSesssion.java index 36d56c921b..8e01c903bd 100644 --- a/spring-websocket/src/main/java/org/springframework/web/socket/adapter/AbstractWebSocketSesssion.java +++ b/spring-websocket/src/main/java/org/springframework/web/socket/adapter/AbstractWebSocketSesssion.java @@ -23,6 +23,8 @@ import org.apache.commons.logging.LogFactory; import org.springframework.util.Assert; import org.springframework.web.socket.BinaryMessage; import org.springframework.web.socket.CloseStatus; +import org.springframework.web.socket.PingMessage; +import org.springframework.web.socket.PongMessage; import org.springframework.web.socket.TextMessage; import org.springframework.web.socket.WebSocketMessage; import org.springframework.web.socket.WebSocketSession; @@ -101,15 +103,24 @@ public abstract class AbstractWebSocketSesssion implements WebSocketSession, else if (message instanceof BinaryMessage) { sendBinaryMessage((BinaryMessage) message); } + else if (message instanceof PingMessage) { + sendPingMessage((PingMessage) message); + } + else if (message instanceof PongMessage) { + sendPongMessage((PongMessage) message); + } else { throw new IllegalStateException("Unexpected WebSocketMessage type: " + message); } } - protected abstract void sendTextMessage(TextMessage message) throws IOException ; + protected abstract void sendTextMessage(TextMessage message) throws IOException; + + protected abstract void sendBinaryMessage(BinaryMessage message) throws IOException; - protected abstract void sendBinaryMessage(BinaryMessage message) throws IOException ; + protected abstract void sendPingMessage(PingMessage message) throws IOException; + protected abstract void sendPongMessage(PongMessage message) throws IOException; @Override public final void close() throws IOException { diff --git a/spring-websocket/src/main/java/org/springframework/web/socket/adapter/JettyWebSocketSession.java b/spring-websocket/src/main/java/org/springframework/web/socket/adapter/JettyWebSocketSession.java index bdf7b57d07..b7bc0a8130 100644 --- a/spring-websocket/src/main/java/org/springframework/web/socket/adapter/JettyWebSocketSession.java +++ b/spring-websocket/src/main/java/org/springframework/web/socket/adapter/JettyWebSocketSession.java @@ -26,6 +26,8 @@ import org.springframework.http.HttpHeaders; import org.springframework.util.ObjectUtils; import org.springframework.web.socket.BinaryMessage; import org.springframework.web.socket.CloseStatus; +import org.springframework.web.socket.PingMessage; +import org.springframework.web.socket.PongMessage; import org.springframework.web.socket.TextMessage; import org.springframework.web.socket.WebSocketSession; @@ -117,6 +119,16 @@ public class JettyWebSocketSession extends AbstractWebSocketSesssion() { + @Override + public void onMessage(javax.websocket.PongMessage message) { + handlePongMessage(session, message.getApplicationData()); + } + }); + try { this.handler.afterConnectionEstablished(this.wsSession); } @@ -118,6 +126,16 @@ public class StandardWebSocketHandlerAdapter extends Endpoint { } } + private void handlePongMessage(javax.websocket.Session session, ByteBuffer payload) { + PongMessage pongMessage = new PongMessage(payload); + try { + this.handler.handleMessage(this.wsSession, pongMessage); + } + catch (Throwable t) { + ExceptionWebSocketHandlerDecorator.tryCloseWithError(this.wsSession, t, logger); + } + } + @Override public void onClose(javax.websocket.Session session, CloseReason reason) { CloseStatus closeStatus = new CloseStatus(reason.getCloseCode().getCode(), reason.getReasonPhrase()); diff --git a/spring-websocket/src/main/java/org/springframework/web/socket/adapter/StandardWebSocketSession.java b/spring-websocket/src/main/java/org/springframework/web/socket/adapter/StandardWebSocketSession.java index 89bbba6294..7ddd3a8718 100644 --- a/spring-websocket/src/main/java/org/springframework/web/socket/adapter/StandardWebSocketSession.java +++ b/spring-websocket/src/main/java/org/springframework/web/socket/adapter/StandardWebSocketSession.java @@ -29,6 +29,8 @@ import org.springframework.http.HttpHeaders; import org.springframework.util.StringUtils; import org.springframework.web.socket.BinaryMessage; import org.springframework.web.socket.CloseStatus; +import org.springframework.web.socket.PingMessage; +import org.springframework.web.socket.PongMessage; import org.springframework.web.socket.TextMessage; import org.springframework.web.socket.WebSocketSession; @@ -121,6 +123,16 @@ public class StandardWebSocketSession extends AbstractWebSocketSesssion