Improve semantics writing currentData

Before this commit, the return value from write was interpreted as the
data being fully written and ready to be released via releaseData().

This is not true for WebSocketSession implementations where a true
return value simply means the message was sent with the full payload
but releas is not appropriate until a send confirmation.

Technically not an issue since WebSocketSession's extending this do
not use pooled buffers. Nevertheless this commit refines the semantics
of write, removes the releaseData() method, and makes sub-classes
responsible for releasing the buffer when fully written (and they
know best when that is). As a bonus currentData is now private.

Issue: SPR-16207
This commit is contained in:
Rossen Stoyanchev
2017-11-23 10:55:03 -05:00
parent 102a0ad792
commit 01a82b5291
6 changed files with 47 additions and 51 deletions

View File

@@ -153,6 +153,9 @@ public abstract class AbstractListenerWebSocketSession<T> extends AbstractWebSoc
/**
* Send the given WebSocket message.
* <p><strong>Note:</strong> Sub-classes are responsible for releasing the
* payload data buffer, once fully written, if pooled buffers apply to the
* underlying container.
*/
protected abstract boolean sendMessage(WebSocketMessage message) throws IOException;
@@ -268,11 +271,6 @@ public abstract class AbstractListenerWebSocketSession<T> extends AbstractWebSoc
return sendMessage(message);
}
@Override
protected void releaseData() {
this.currentData = null;
}
@Override
protected boolean isDataEmpty(WebSocketMessage message) {
return (message.getPayload().readableByteCount() == 0);
@@ -280,7 +278,7 @@ public abstract class AbstractListenerWebSocketSession<T> extends AbstractWebSoc
@Override
protected boolean isWritePossible() {
return (this.isReady && this.currentData != null);
return (this.isReady);
}
/**

View File

@@ -27,7 +27,9 @@ import io.undertow.websockets.core.WebSockets;
import reactor.core.publisher.Mono;
import reactor.core.publisher.MonoProcessor;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferFactory;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.lang.Nullable;
import org.springframework.util.ObjectUtils;
import org.springframework.web.reactive.socket.CloseStatus;
@@ -78,19 +80,19 @@ public class UndertowWebSocketSession extends AbstractListenerWebSocketSession<W
if (WebSocketMessage.Type.TEXT.equals(message.getType())) {
getSendProcessor().setReadyToSend(false);
String text = new String(buffer.array(), StandardCharsets.UTF_8);
WebSockets.sendText(text, getDelegate(), new SendProcessorCallback());
WebSockets.sendText(text, getDelegate(), new SendProcessorCallback(message.getPayload()));
}
else if (WebSocketMessage.Type.BINARY.equals(message.getType())) {
getSendProcessor().setReadyToSend(false);
WebSockets.sendBinary(buffer, getDelegate(), new SendProcessorCallback());
WebSockets.sendBinary(buffer, getDelegate(), new SendProcessorCallback(message.getPayload()));
}
else if (WebSocketMessage.Type.PING.equals(message.getType())) {
getSendProcessor().setReadyToSend(false);
WebSockets.sendPing(buffer, getDelegate(), new SendProcessorCallback());
WebSockets.sendPing(buffer, getDelegate(), new SendProcessorCallback(message.getPayload()));
}
else if (WebSocketMessage.Type.PONG.equals(message.getType())) {
getSendProcessor().setReadyToSend(false);
WebSockets.sendPong(buffer, getDelegate(), new SendProcessorCallback());
WebSockets.sendPong(buffer, getDelegate(), new SendProcessorCallback(message.getPayload()));
}
else {
throw new IllegalArgumentException("Unexpected message type: " + message.getType());
@@ -110,14 +112,22 @@ public class UndertowWebSocketSession extends AbstractListenerWebSocketSession<W
private final class SendProcessorCallback implements WebSocketCallback<Void> {
private final DataBuffer payload;
SendProcessorCallback(DataBuffer payload) {
this.payload = payload;
}
@Override
public void complete(WebSocketChannel channel, Void context) {
DataBufferUtils.release(this.payload);
getSendProcessor().setReadyToSend(true);
getSendProcessor().onWritePossible();
}
@Override
public void onError(WebSocketChannel channel, Void context, Throwable throwable) {
DataBufferUtils.release(this.payload);
getSendProcessor().cancel();
getSendProcessor().onError(throwable);
}