AbstractListenerWebSocketSession: suspend the channel when there is no demand
Issues: SPR-16207
This commit is contained in:
parent
06b2ab3908
commit
d8099adc9a
|
|
@ -17,6 +17,7 @@
|
|||
package org.springframework.web.reactive.socket.adapter;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import org.reactivestreams.Publisher;
|
||||
|
|
@ -25,6 +26,7 @@ import org.reactivestreams.Subscription;
|
|||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.core.publisher.MonoProcessor;
|
||||
import reactor.util.concurrent.Queues;
|
||||
|
||||
import org.springframework.core.io.buffer.DataBufferFactory;
|
||||
import org.springframework.http.server.reactive.AbstractListenerReadPublisher;
|
||||
|
|
@ -147,6 +149,17 @@ public abstract class AbstractListenerWebSocketSession<T> extends AbstractWebSoc
|
|||
*/
|
||||
protected abstract void resumeReceiving();
|
||||
|
||||
/**
|
||||
* Returns {@code true} if receiving new message(s) is suspended otherwise
|
||||
* {@code false}.
|
||||
* <p><strong>Note:</strong> if the underlying WebSocket API does not provide
|
||||
* flow control for receiving messages, and this method should return
|
||||
* {@code false} and {@link #canSuspendReceiving()} should return {@code false}.
|
||||
* @return returns {@code true} if receiving new message(s) is suspended
|
||||
* otherwise {@code false}.
|
||||
*/
|
||||
protected abstract boolean isSuspended();
|
||||
|
||||
/**
|
||||
* Send the given WebSocket message.
|
||||
*/
|
||||
|
|
@ -213,32 +226,39 @@ public abstract class AbstractListenerWebSocketSession<T> extends AbstractWebSoc
|
|||
|
||||
private final class WebSocketReceivePublisher extends AbstractListenerReadPublisher<WebSocketMessage> {
|
||||
|
||||
@Nullable
|
||||
private volatile WebSocketMessage webSocketMessage;
|
||||
private volatile Queue<Object> pendingWebSocketMessages = Queues.unbounded().get();
|
||||
|
||||
@Override
|
||||
protected void checkOnDataAvailable() {
|
||||
if (this.webSocketMessage != null) {
|
||||
if (isSuspended()) {
|
||||
resumeReceiving();
|
||||
}
|
||||
if (!pendingWebSocketMessages.isEmpty()) {
|
||||
onDataAvailable();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void suspendReading() {
|
||||
suspendReceiving();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
protected WebSocketMessage read() throws IOException {
|
||||
if (this.webSocketMessage != null) {
|
||||
WebSocketMessage result = this.webSocketMessage;
|
||||
this.webSocketMessage = null;
|
||||
resumeReceiving();
|
||||
return result;
|
||||
}
|
||||
return (WebSocketMessage) pendingWebSocketMessages.poll();
|
||||
}
|
||||
|
||||
return null;
|
||||
@Override
|
||||
public void onAllDataRead() {
|
||||
if (isSuspended()) {
|
||||
resumeReceiving();
|
||||
}
|
||||
super.onAllDataRead();
|
||||
}
|
||||
|
||||
void handleMessage(WebSocketMessage webSocketMessage) {
|
||||
this.webSocketMessage = webSocketMessage;
|
||||
suspendReceiving();
|
||||
this.pendingWebSocketMessages.offer(webSocketMessage);
|
||||
onDataAvailable();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -79,6 +79,11 @@ public class JettyWebSocketSession extends AbstractListenerWebSocketSession<Sess
|
|||
this.suspendToken = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isSuspended() {
|
||||
return this.suspendToken != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean sendMessage(WebSocketMessage message) throws IOException {
|
||||
ByteBuffer buffer = message.getPayload().asByteBuffer();
|
||||
|
|
|
|||
|
|
@ -71,6 +71,11 @@ public class StandardWebSocketSession extends AbstractListenerWebSocketSession<S
|
|||
// no-op
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isSuspended() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean sendMessage(WebSocketMessage message) throws IOException {
|
||||
ByteBuffer buffer = message.getPayload().asByteBuffer();
|
||||
|
|
|
|||
|
|
@ -16,6 +16,8 @@
|
|||
|
||||
package org.springframework.web.reactive.socket.adapter;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
|
||||
|
||||
import javax.websocket.Session;
|
||||
|
||||
import org.apache.tomcat.websocket.WsSession;
|
||||
|
|
@ -33,6 +35,9 @@ import reactor.core.publisher.MonoProcessor;
|
|||
* @since 5.0
|
||||
*/
|
||||
public class TomcatWebSocketSession extends StandardWebSocketSession {
|
||||
private static final AtomicIntegerFieldUpdater<TomcatWebSocketSession> SUSPENDED =
|
||||
AtomicIntegerFieldUpdater.newUpdater(TomcatWebSocketSession.class, "suspended");
|
||||
private volatile int suspended;
|
||||
|
||||
|
||||
public TomcatWebSocketSession(Session session, HandshakeInfo info, DataBufferFactory factory) {
|
||||
|
|
@ -53,12 +58,21 @@ public class TomcatWebSocketSession extends StandardWebSocketSession {
|
|||
|
||||
@Override
|
||||
protected void suspendReceiving() {
|
||||
((WsSession) getDelegate()).suspend();
|
||||
if (SUSPENDED.compareAndSet(this, 0, 1)) {
|
||||
((WsSession) getDelegate()).suspend();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void resumeReceiving() {
|
||||
((WsSession) getDelegate()).resume();
|
||||
if (SUSPENDED.compareAndSet(this, 1, 0)) {
|
||||
((WsSession) getDelegate()).resume();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isSuspended() {
|
||||
return this.suspended == 1;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -61,14 +61,21 @@ public class UndertowWebSocketSession extends AbstractListenerWebSocketSession<W
|
|||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void suspendReceiving() {
|
||||
getDelegate().suspendReceives();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void resumeReceiving() {
|
||||
getDelegate().resumeReceives();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isSuspended() {
|
||||
return !getDelegate().isReceivesResumed();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean sendMessage(WebSocketMessage message) throws IOException {
|
||||
ByteBuffer buffer = message.getPayload().asByteBuffer();
|
||||
|
|
|
|||
Loading…
Reference in New Issue