Browse Source

Avoid deadlock between SockJS heartbeat and XHR polling

Issue: SPR-14833
pull/1226/head
Juergen Hoeller 8 years ago
parent
commit
72e1f7e898
  1. 19
      spring-websocket/src/main/java/org/springframework/web/socket/sockjs/transport/session/AbstractHttpSockJsSession.java
  2. 12
      spring-websocket/src/main/java/org/springframework/web/socket/sockjs/transport/session/AbstractSockJsSession.java

19
spring-websocket/src/main/java/org/springframework/web/socket/sockjs/transport/session/AbstractHttpSockJsSession.java

@ -51,6 +51,7 @@ import org.springframework.web.socket.sockjs.transport.SockJsServiceConfig; @@ -51,6 +51,7 @@ import org.springframework.web.socket.sockjs.transport.SockJsServiceConfig;
*/
public abstract class AbstractHttpSockJsSession extends AbstractSockJsSession {
private final Queue<String> messageCache;
private volatile URI uri;
@ -64,20 +65,13 @@ public abstract class AbstractHttpSockJsSession extends AbstractSockJsSession { @@ -64,20 +65,13 @@ public abstract class AbstractHttpSockJsSession extends AbstractSockJsSession {
private volatile String acceptedProtocol;
private volatile ServerHttpResponse response;
private volatile SockJsFrameFormat frameFormat;
private volatile ServerHttpAsyncRequestControl asyncRequestControl;
private final Object responseLock = new Object();
private volatile boolean readyToSend;
private final Queue<String> messageCache;
private boolean readyToSend;
public AbstractHttpSockJsSession(String id, SockJsServiceConfig config,
@ -209,14 +203,10 @@ public abstract class AbstractHttpSockJsSession extends AbstractSockJsSession { @@ -209,14 +203,10 @@ public abstract class AbstractHttpSockJsSession extends AbstractSockJsSession {
this.frameFormat = frameFormat;
this.asyncRequestControl = request.getAsyncRequestControl(response);
this.asyncRequestControl.start(-1);
disableShallowEtagHeaderFilter(request);
// Let "our" handler know before sending the open frame to the remote handler
delegateConnectionEstablished();
handleRequestInternal(request, response, true);
// Request might have been reset (e.g. polling sessions do after writing)
this.readyToSend = isActive();
}
@ -252,9 +242,7 @@ public abstract class AbstractHttpSockJsSession extends AbstractSockJsSession { @@ -252,9 +242,7 @@ public abstract class AbstractHttpSockJsSession extends AbstractSockJsSession {
this.frameFormat = frameFormat;
this.asyncRequestControl = request.getAsyncRequestControl(response);
this.asyncRequestControl.start(-1);
disableShallowEtagHeaderFilter(request);
handleRequestInternal(request, response, false);
this.readyToSend = isActive();
}
@ -318,14 +306,11 @@ public abstract class AbstractHttpSockJsSession extends AbstractSockJsSession { @@ -318,14 +306,11 @@ public abstract class AbstractHttpSockJsSession extends AbstractSockJsSession {
protected void resetRequest() {
synchronized (this.responseLock) {
ServerHttpAsyncRequestControl control = this.asyncRequestControl;
this.asyncRequestControl = null;
this.readyToSend = false;
this.response = null;
updateLastActiveTime();
if (control != null && !control.isCompleted()) {
if (control.isStarted()) {
try {

12
spring-websocket/src/main/java/org/springframework/web/socket/sockjs/transport/session/AbstractSockJsSession.java

@ -91,6 +91,8 @@ public abstract class AbstractSockJsSession implements SockJsSession { @@ -91,6 +91,8 @@ public abstract class AbstractSockJsSession implements SockJsSession {
protected final Log logger = LogFactory.getLog(getClass());
protected final Object responseLock = new Object();
private final String id;
private final SockJsServiceConfig config;
@ -109,8 +111,6 @@ public abstract class AbstractSockJsSession implements SockJsSession { @@ -109,8 +111,6 @@ public abstract class AbstractSockJsSession implements SockJsSession {
private HeartbeatTask heartbeatTask;
private final Object heartbeatLock = new Object();
private volatile boolean heartbeatDisabled;
@ -250,7 +250,7 @@ public abstract class AbstractSockJsSession implements SockJsSession { @@ -250,7 +250,7 @@ public abstract class AbstractSockJsSession implements SockJsSession {
}
protected void sendHeartbeat() throws SockJsTransportFailureException {
synchronized (this.heartbeatLock) {
synchronized (this.responseLock) {
if (isActive() && !this.heartbeatDisabled) {
writeFrame(SockJsFrame.heartbeatFrame());
scheduleHeartbeat();
@ -262,7 +262,7 @@ public abstract class AbstractSockJsSession implements SockJsSession { @@ -262,7 +262,7 @@ public abstract class AbstractSockJsSession implements SockJsSession {
if (this.heartbeatDisabled) {
return;
}
synchronized (this.heartbeatLock) {
synchronized (this.responseLock) {
cancelHeartbeat();
if (!isActive()) {
return;
@ -277,7 +277,7 @@ public abstract class AbstractSockJsSession implements SockJsSession { @@ -277,7 +277,7 @@ public abstract class AbstractSockJsSession implements SockJsSession {
}
protected void cancelHeartbeat() {
synchronized (this.heartbeatLock) {
synchronized (this.responseLock) {
if (this.heartbeatFuture != null) {
if (logger.isTraceEnabled()) {
logger.trace("Cancelling heartbeat in session " + getId());
@ -445,7 +445,7 @@ public abstract class AbstractSockJsSession implements SockJsSession { @@ -445,7 +445,7 @@ public abstract class AbstractSockJsSession implements SockJsSession {
@Override
public void run() {
synchronized (heartbeatLock) {
synchronized (responseLock) {
if (!this.expired) {
try {
sendHeartbeat();

Loading…
Cancel
Save