Merge pull request #3253 from lachlan-roberts/jetty-10.0.x-3167-2175-websocket-close
Issue #3167 - Refactor of WebSocketChannelState
This commit is contained in:
commit
a2d3a81cef
|
@ -55,8 +55,6 @@ abstract public class WriteFlusher
|
||||||
private final EndPoint _endPoint;
|
private final EndPoint _endPoint;
|
||||||
private final AtomicReference<State> _state = new AtomicReference<>();
|
private final AtomicReference<State> _state = new AtomicReference<>();
|
||||||
|
|
||||||
Throwable last;
|
|
||||||
|
|
||||||
static
|
static
|
||||||
{
|
{
|
||||||
// fill the state machine
|
// fill the state machine
|
||||||
|
@ -272,13 +270,7 @@ abstract public class WriteFlusher
|
||||||
LOG.debug("write: {} {}", this, BufferUtil.toDetailString(buffers));
|
LOG.debug("write: {} {}", this, BufferUtil.toDetailString(buffers));
|
||||||
|
|
||||||
if (!updateState(__IDLE, __WRITING))
|
if (!updateState(__IDLE, __WRITING))
|
||||||
{
|
|
||||||
if (last!=null)
|
|
||||||
last.printStackTrace();
|
|
||||||
throw new WritePendingException();
|
throw new WritePendingException();
|
||||||
}
|
|
||||||
|
|
||||||
last = new Throwable();
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
|
|
@ -18,6 +18,18 @@
|
||||||
|
|
||||||
package org.eclipse.jetty.websocket.client;
|
package org.eclipse.jetty.websocket.client;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.CookieStore;
|
||||||
|
import java.net.SocketAddress;
|
||||||
|
import java.net.URI;
|
||||||
|
import java.time.Duration;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
import java.util.concurrent.Executor;
|
||||||
|
import java.util.concurrent.ThreadLocalRandom;
|
||||||
|
|
||||||
import org.eclipse.jetty.client.HttpClient;
|
import org.eclipse.jetty.client.HttpClient;
|
||||||
import org.eclipse.jetty.io.ByteBufferPool;
|
import org.eclipse.jetty.io.ByteBufferPool;
|
||||||
import org.eclipse.jetty.util.DecoratedObjectFactory;
|
import org.eclipse.jetty.util.DecoratedObjectFactory;
|
||||||
|
@ -34,18 +46,6 @@ import org.eclipse.jetty.websocket.common.JettyWebSocketFrameHandlerFactory;
|
||||||
import org.eclipse.jetty.websocket.core.WebSocketExtensionRegistry;
|
import org.eclipse.jetty.websocket.core.WebSocketExtensionRegistry;
|
||||||
import org.eclipse.jetty.websocket.core.client.WebSocketCoreClient;
|
import org.eclipse.jetty.websocket.core.client.WebSocketCoreClient;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.net.CookieStore;
|
|
||||||
import java.net.SocketAddress;
|
|
||||||
import java.net.URI;
|
|
||||||
import java.time.Duration;
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.concurrent.CompletableFuture;
|
|
||||||
import java.util.concurrent.Executor;
|
|
||||||
import java.util.concurrent.ThreadLocalRandom;
|
|
||||||
|
|
||||||
public class WebSocketClient extends ContainerLifeCycle implements WebSocketPolicy
|
public class WebSocketClient extends ContainerLifeCycle implements WebSocketPolicy
|
||||||
{
|
{
|
||||||
private final WebSocketCoreClient coreClient;
|
private final WebSocketCoreClient coreClient;
|
||||||
|
@ -93,9 +93,7 @@ public class WebSocketClient extends ContainerLifeCycle implements WebSocketPoli
|
||||||
|
|
||||||
public CompletableFuture<Session> connect(Object websocket, URI toUri) throws IOException
|
public CompletableFuture<Session> connect(Object websocket, URI toUri) throws IOException
|
||||||
{
|
{
|
||||||
ClientUpgradeRequestImpl upgradeRequest = new ClientUpgradeRequestImpl(this, coreClient, null, toUri, websocket);
|
return connect(websocket, toUri, null);
|
||||||
coreClient.connect(upgradeRequest);
|
|
||||||
return upgradeRequest.getFutureSession();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -107,7 +105,7 @@ public class WebSocketClient extends ContainerLifeCycle implements WebSocketPoli
|
||||||
* @return the future for the session, available on success of connect
|
* @return the future for the session, available on success of connect
|
||||||
* @throws IOException if unable to connect
|
* @throws IOException if unable to connect
|
||||||
*/
|
*/
|
||||||
public CompletableFuture<Session> connect(Object websocket, URI toUri, org.eclipse.jetty.websocket.api.UpgradeRequest request) throws IOException
|
public CompletableFuture<Session> connect(Object websocket, URI toUri, UpgradeRequest request) throws IOException
|
||||||
{
|
{
|
||||||
ClientUpgradeRequestImpl upgradeRequest = new ClientUpgradeRequestImpl(this, coreClient, request, toUri, websocket);
|
ClientUpgradeRequestImpl upgradeRequest = new ClientUpgradeRequestImpl(this, coreClient, request, toUri, websocket);
|
||||||
coreClient.connect(upgradeRequest);
|
coreClient.connect(upgradeRequest);
|
||||||
|
|
|
@ -18,6 +18,15 @@
|
||||||
|
|
||||||
package org.eclipse.jetty.websocket.core.internal;
|
package org.eclipse.jetty.websocket.core.internal;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayDeque;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.ListIterator;
|
||||||
|
import java.util.Queue;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import org.eclipse.jetty.io.ByteBufferPool;
|
import org.eclipse.jetty.io.ByteBufferPool;
|
||||||
import org.eclipse.jetty.util.Callback;
|
import org.eclipse.jetty.util.Callback;
|
||||||
import org.eclipse.jetty.util.DecoratedObjectFactory;
|
import org.eclipse.jetty.util.DecoratedObjectFactory;
|
||||||
|
@ -34,15 +43,6 @@ import org.eclipse.jetty.websocket.core.IncomingFrames;
|
||||||
import org.eclipse.jetty.websocket.core.OutgoingFrames;
|
import org.eclipse.jetty.websocket.core.OutgoingFrames;
|
||||||
import org.eclipse.jetty.websocket.core.WebSocketExtensionRegistry;
|
import org.eclipse.jetty.websocket.core.WebSocketExtensionRegistry;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.ArrayDeque;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.ListIterator;
|
|
||||||
import java.util.Queue;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents the stack of Extensions.
|
* Represents the stack of Extensions.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -18,6 +18,13 @@
|
||||||
|
|
||||||
package org.eclipse.jetty.websocket.core.internal;
|
package org.eclipse.jetty.websocket.core.internal;
|
||||||
|
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.util.ArrayDeque;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Deque;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
import org.eclipse.jetty.io.ByteBufferPool;
|
import org.eclipse.jetty.io.ByteBufferPool;
|
||||||
import org.eclipse.jetty.io.EndPoint;
|
import org.eclipse.jetty.io.EndPoint;
|
||||||
import org.eclipse.jetty.util.BufferUtil;
|
import org.eclipse.jetty.util.BufferUtil;
|
||||||
|
@ -28,14 +35,6 @@ import org.eclipse.jetty.util.log.Logger;
|
||||||
import org.eclipse.jetty.websocket.core.Frame;
|
import org.eclipse.jetty.websocket.core.Frame;
|
||||||
import org.eclipse.jetty.websocket.core.OpCode;
|
import org.eclipse.jetty.websocket.core.OpCode;
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
import java.nio.channels.ClosedChannelException;
|
|
||||||
import java.util.ArrayDeque;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Deque;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Objects;
|
|
||||||
|
|
||||||
public class FrameFlusher extends IteratingCallback
|
public class FrameFlusher extends IteratingCallback
|
||||||
{
|
{
|
||||||
public static final Frame FLUSH_FRAME = new Frame(OpCode.BINARY);
|
public static final Frame FLUSH_FRAME = new Frame(OpCode.BINARY);
|
||||||
|
@ -148,7 +147,7 @@ public class FrameFlusher extends IteratingCallback
|
||||||
}
|
}
|
||||||
|
|
||||||
if (LOG.isDebugEnabled())
|
if (LOG.isDebugEnabled())
|
||||||
LOG.debug("{} processed {} entries flush=%b batch=%s: {}",
|
LOG.debug("{} processed {} entries flush={} batch={}: {}",
|
||||||
this,
|
this,
|
||||||
entries.size(),
|
entries.size(),
|
||||||
flush,
|
flush,
|
||||||
|
@ -269,9 +268,8 @@ public class FrameFlusher extends IteratingCallback
|
||||||
@Override
|
@Override
|
||||||
public String toString()
|
public String toString()
|
||||||
{
|
{
|
||||||
return String.format("%s@%x[queueSize=%d,aggregate=%s]",
|
return String.format("%s[queueSize=%d,aggregate=%s]",
|
||||||
getClass().getSimpleName(),
|
super.toString(),
|
||||||
hashCode(),
|
|
||||||
getQueueSize(),
|
getQueueSize(),
|
||||||
BufferUtil.toDetailString(batchBuffer));
|
BufferUtil.toDetailString(batchBuffer));
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,8 +18,17 @@
|
||||||
|
|
||||||
package org.eclipse.jetty.websocket.core.internal;
|
package org.eclipse.jetty.websocket.core.internal;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.SocketAddress;
|
||||||
|
import java.net.SocketException;
|
||||||
|
import java.net.SocketTimeoutException;
|
||||||
|
import java.net.URI;
|
||||||
|
import java.time.Duration;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.Executor;
|
||||||
|
|
||||||
import org.eclipse.jetty.io.ByteBufferPool;
|
import org.eclipse.jetty.io.ByteBufferPool;
|
||||||
import org.eclipse.jetty.io.EofException;
|
|
||||||
import org.eclipse.jetty.util.Callback;
|
import org.eclipse.jetty.util.Callback;
|
||||||
import org.eclipse.jetty.util.Utf8Appendable;
|
import org.eclipse.jetty.util.Utf8Appendable;
|
||||||
import org.eclipse.jetty.util.component.Dumpable;
|
import org.eclipse.jetty.util.component.Dumpable;
|
||||||
|
@ -40,16 +49,6 @@ import org.eclipse.jetty.websocket.core.WebSocketConstants;
|
||||||
import org.eclipse.jetty.websocket.core.WebSocketTimeoutException;
|
import org.eclipse.jetty.websocket.core.WebSocketTimeoutException;
|
||||||
import org.eclipse.jetty.websocket.core.internal.Parser.ParsedFrame;
|
import org.eclipse.jetty.websocket.core.internal.Parser.ParsedFrame;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.net.SocketAddress;
|
|
||||||
import java.net.SocketException;
|
|
||||||
import java.net.SocketTimeoutException;
|
|
||||||
import java.net.URI;
|
|
||||||
import java.time.Duration;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.concurrent.Executor;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The Core WebSocket Session.
|
* The Core WebSocket Session.
|
||||||
*/
|
*/
|
||||||
|
@ -59,11 +58,10 @@ public class WebSocketChannel implements IncomingFrames, FrameHandler.CoreSessio
|
||||||
private final static CloseStatus NO_CODE = new CloseStatus(CloseStatus.NO_CODE);
|
private final static CloseStatus NO_CODE = new CloseStatus(CloseStatus.NO_CODE);
|
||||||
|
|
||||||
private final Behavior behavior;
|
private final Behavior behavior;
|
||||||
private final WebSocketChannelState state = new WebSocketChannelState();
|
private final WebSocketChannelState channelState = new WebSocketChannelState();
|
||||||
private final FrameHandler handler;
|
private final FrameHandler handler;
|
||||||
private final Negotiated negotiated;
|
private final Negotiated negotiated;
|
||||||
private final boolean demanding;
|
private final boolean demanding;
|
||||||
private final FrameSequence outgoingSequence = new FrameSequence();
|
|
||||||
|
|
||||||
private WebSocketConnection connection;
|
private WebSocketConnection connection;
|
||||||
private boolean autoFragment = WebSocketConstants.DEFAULT_AUTO_FRAGMENT;
|
private boolean autoFragment = WebSocketConstants.DEFAULT_AUTO_FRAGMENT;
|
||||||
|
@ -155,7 +153,7 @@ public class WebSocketChannel implements IncomingFrames, FrameHandler.CoreSessio
|
||||||
if (frame.getOpCode() == OpCode.CLOSE)
|
if (frame.getOpCode() == OpCode.CLOSE)
|
||||||
{
|
{
|
||||||
if (!(frame instanceof ParsedFrame)) // already check in parser
|
if (!(frame instanceof ParsedFrame)) // already check in parser
|
||||||
new CloseStatus(frame.getPayload());
|
CloseStatus.getCloseStatus(frame);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -239,7 +237,7 @@ public class WebSocketChannel implements IncomingFrames, FrameHandler.CoreSessio
|
||||||
@Override
|
@Override
|
||||||
public boolean isOpen()
|
public boolean isOpen()
|
||||||
{
|
{
|
||||||
return state.isOutOpen();
|
return channelState.isOutOpen();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setWebSocketConnection(WebSocketConnection connection)
|
public void setWebSocketConnection(WebSocketConnection connection)
|
||||||
|
@ -289,7 +287,7 @@ public class WebSocketChannel implements IncomingFrames, FrameHandler.CoreSessio
|
||||||
|
|
||||||
public void onClosed(Throwable cause, CloseStatus closeStatus)
|
public void onClosed(Throwable cause, CloseStatus closeStatus)
|
||||||
{
|
{
|
||||||
if (state.onClosed(closeStatus))
|
if (channelState.onClosed(closeStatus))
|
||||||
{
|
{
|
||||||
connection.cancelDemand();
|
connection.cancelDemand();
|
||||||
|
|
||||||
|
@ -390,7 +388,7 @@ public class WebSocketChannel implements IncomingFrames, FrameHandler.CoreSessio
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// Upgrade success
|
// Upgrade success
|
||||||
state.onConnected();
|
channelState.onConnected();
|
||||||
|
|
||||||
if (LOG.isDebugEnabled())
|
if (LOG.isDebugEnabled())
|
||||||
LOG.debug("ConnectionState: Transition to CONNECTED");
|
LOG.debug("ConnectionState: Transition to CONNECTED");
|
||||||
|
@ -398,7 +396,7 @@ public class WebSocketChannel implements IncomingFrames, FrameHandler.CoreSessio
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// Open connection and handler
|
// Open connection and handler
|
||||||
state.onOpen();
|
channelState.onOpen();
|
||||||
handler.onOpen(this);
|
handler.onOpen(this);
|
||||||
if (!demanding)
|
if (!demanding)
|
||||||
connection.demand(1);
|
connection.demand(1);
|
||||||
|
@ -461,10 +459,11 @@ public class WebSocketChannel implements IncomingFrames, FrameHandler.CoreSessio
|
||||||
if (LOG.isDebugEnabled())
|
if (LOG.isDebugEnabled())
|
||||||
LOG.debug("sendFrame({}, {}, {})", frame, callback, batch);
|
LOG.debug("sendFrame({}, {}, {})", frame, callback, batch);
|
||||||
|
|
||||||
|
boolean closed;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
assertValidOutgoing(frame);
|
assertValidOutgoing(frame);
|
||||||
outgoingSequence.check(frame.getOpCode(), frame.isFin());
|
closed = channelState.checkOutgoing(frame);
|
||||||
}
|
}
|
||||||
catch (Throwable ex)
|
catch (Throwable ex)
|
||||||
{
|
{
|
||||||
|
@ -474,11 +473,10 @@ public class WebSocketChannel implements IncomingFrames, FrameHandler.CoreSessio
|
||||||
|
|
||||||
if (frame.getOpCode() == OpCode.CLOSE)
|
if (frame.getOpCode() == OpCode.CLOSE)
|
||||||
{
|
{
|
||||||
CloseStatus closeStatus = CloseStatus.getCloseStatus(frame);
|
|
||||||
if (LOG.isDebugEnabled())
|
if (LOG.isDebugEnabled())
|
||||||
LOG.debug("close({}, {}, {})", closeStatus, callback, batch);
|
LOG.debug("close({}, {}, {})", CloseStatus.getCloseStatus(frame), callback, batch);
|
||||||
|
|
||||||
if (state.onCloseOut(closeStatus))
|
if (closed)
|
||||||
{
|
{
|
||||||
callback = new Callback.Nested(callback)
|
callback = new Callback.Nested(callback)
|
||||||
{
|
{
|
||||||
|
@ -487,7 +485,7 @@ public class WebSocketChannel implements IncomingFrames, FrameHandler.CoreSessio
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
handler.onClosed(state.getCloseStatus());
|
handler.onClosed(channelState.getCloseStatus());
|
||||||
}
|
}
|
||||||
catch (Throwable e)
|
catch (Throwable e)
|
||||||
{
|
{
|
||||||
|
@ -604,8 +602,7 @@ public class WebSocketChannel implements IncomingFrames, FrameHandler.CoreSessio
|
||||||
maxTextMessageSize = maxSize;
|
maxTextMessageSize = maxSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
private class
|
private class IncomingAdaptor implements IncomingFrames
|
||||||
IncomingAdaptor extends FrameSequence implements IncomingFrames
|
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
public void onFrame(Frame frame, Callback callback)
|
public void onFrame(Frame frame, Callback callback)
|
||||||
|
@ -614,59 +611,52 @@ public class WebSocketChannel implements IncomingFrames, FrameHandler.CoreSessio
|
||||||
{
|
{
|
||||||
if (LOG.isDebugEnabled())
|
if (LOG.isDebugEnabled())
|
||||||
LOG.debug("receiveFrame({}, {}) - connectionState={}, handler={}",
|
LOG.debug("receiveFrame({}, {}) - connectionState={}, handler={}",
|
||||||
frame, callback, state, handler);
|
frame, callback, channelState, handler);
|
||||||
|
|
||||||
check(frame.getOpCode(), frame.isFin());
|
|
||||||
if (state.isInOpen())
|
boolean closed = channelState.checkIncoming(frame);
|
||||||
|
|
||||||
|
// Handle inbound close
|
||||||
|
if (frame.getOpCode() == OpCode.CLOSE)
|
||||||
{
|
{
|
||||||
// Handle inbound close
|
connection.cancelDemand();
|
||||||
if (frame.getOpCode() == OpCode.CLOSE)
|
if (closed)
|
||||||
{
|
{
|
||||||
connection.cancelDemand();
|
|
||||||
CloseStatus closeStatus = CloseStatus.getCloseStatus(frame);
|
|
||||||
if (state.onCloseIn(closeStatus))
|
|
||||||
{
|
|
||||||
callback = new Callback.Nested(callback)
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public void completed()
|
|
||||||
{
|
|
||||||
handler.onClosed(state.getCloseStatus());
|
|
||||||
connection.close();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
handler.onFrame(frame, callback);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
callback = new Callback.Nested(callback)
|
callback = new Callback.Nested(callback)
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
public void completed()
|
public void completed()
|
||||||
{
|
{
|
||||||
// was a close sent by the handler?
|
handler.onClosed(channelState.getCloseStatus());
|
||||||
if (state.isOutOpen())
|
connection.close();
|
||||||
{
|
|
||||||
// No!
|
|
||||||
if (LOG.isDebugEnabled())
|
|
||||||
LOG.debug("ConnectionState: sending close response {}", closeStatus);
|
|
||||||
|
|
||||||
close(closeStatus.getCode(), closeStatus.getReason(), Callback.NOOP);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
handler.onFrame(frame, callback);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle the frame
|
callback = new Callback.Nested(callback)
|
||||||
handler.onFrame(frame, callback);
|
{
|
||||||
}
|
@Override
|
||||||
else
|
public void completed()
|
||||||
{
|
{
|
||||||
if (LOG.isDebugEnabled())
|
if (channelState.isOutOpen())
|
||||||
LOG.debug("Discarding post EOF frame - {}", frame);
|
{
|
||||||
callback.failed(new EofException());
|
CloseStatus closeStatus = CloseStatus.getCloseStatus(frame);
|
||||||
|
|
||||||
|
if (LOG.isDebugEnabled())
|
||||||
|
LOG.debug("ConnectionState: sending close response {}", closeStatus);
|
||||||
|
|
||||||
|
// this may race with a rare application close but errors are ignored
|
||||||
|
close(closeStatus.getCode(), closeStatus.getReason(), Callback.NOOP);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handle the frame
|
||||||
|
handler.onFrame(frame, callback);
|
||||||
}
|
}
|
||||||
catch (Throwable t)
|
catch (Throwable t)
|
||||||
{
|
{
|
||||||
|
@ -747,7 +737,7 @@ public class WebSocketChannel implements IncomingFrames, FrameHandler.CoreSessio
|
||||||
{
|
{
|
||||||
return String.format("WSChannel@%x{%s,%s,af=%b,i/o=%d/%d,fs=%d}->%s",
|
return String.format("WSChannel@%x{%s,%s,af=%b,i/o=%d/%d,fs=%d}->%s",
|
||||||
hashCode(),
|
hashCode(),
|
||||||
state,
|
channelState,
|
||||||
negotiated,
|
negotiated,
|
||||||
autoFragment,
|
autoFragment,
|
||||||
inputBufferSize,
|
inputBufferSize,
|
||||||
|
|
|
@ -19,140 +19,197 @@
|
||||||
package org.eclipse.jetty.websocket.core.internal;
|
package org.eclipse.jetty.websocket.core.internal;
|
||||||
|
|
||||||
import org.eclipse.jetty.websocket.core.CloseStatus;
|
import org.eclipse.jetty.websocket.core.CloseStatus;
|
||||||
|
import org.eclipse.jetty.websocket.core.Frame;
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
import org.eclipse.jetty.websocket.core.OpCode;
|
||||||
|
import org.eclipse.jetty.websocket.core.ProtocolException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Atomic Connection State
|
* Atomic Connection State
|
||||||
*/
|
*/
|
||||||
public class WebSocketChannelState
|
public class WebSocketChannelState
|
||||||
{
|
{
|
||||||
private static class State
|
enum State
|
||||||
{
|
{
|
||||||
final String name;
|
CONNECTING,
|
||||||
final boolean inOpen;
|
CONNECTED,
|
||||||
final boolean outOpen;
|
OPEN,
|
||||||
final CloseStatus closeStatus;
|
ICLOSED,
|
||||||
|
OCLOSED,
|
||||||
State(String name, boolean inOpen, boolean outOpen, CloseStatus closeStatus)
|
CLOSED
|
||||||
{
|
|
||||||
this.name = name;
|
|
||||||
this.inOpen = inOpen;
|
|
||||||
this.outOpen = outOpen;
|
|
||||||
this.closeStatus = closeStatus;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString()
|
|
||||||
{
|
|
||||||
return String.format("%s{i=%b o=%b c=%d}", name, inOpen, outOpen, closeStatus == null?-1:closeStatus.getCode());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final State CONNECTING = new State("CONNECTING", false, false, null);
|
private State _channelState = State.CONNECTING;
|
||||||
private static final State CONNECTED = new State("CONNECTED", true, true, null);
|
private byte _incomingContinuation = OpCode.UNDEFINED;
|
||||||
private static final State OPEN = new State("OPEN", true, true, null);
|
private byte _outgoingContinuation = OpCode.UNDEFINED;
|
||||||
|
CloseStatus _closeStatus = null;
|
||||||
private AtomicReference<State> state = new AtomicReference<>(CONNECTING);
|
|
||||||
|
|
||||||
public void onConnected()
|
public void onConnected()
|
||||||
{
|
{
|
||||||
if (!state.compareAndSet(CONNECTING, CONNECTED))
|
synchronized (this)
|
||||||
throw new IllegalStateException(state.get().toString());
|
{
|
||||||
|
if (_channelState != State.CONNECTING)
|
||||||
|
throw new IllegalStateException(_channelState.toString());
|
||||||
|
|
||||||
|
_channelState = State.CONNECTED;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onOpen()
|
public void onOpen()
|
||||||
{
|
{
|
||||||
if (!state.compareAndSet(CONNECTED, OPEN))
|
synchronized (this)
|
||||||
throw new IllegalStateException(state.get().toString());
|
{
|
||||||
|
if (_channelState != State.CONNECTED)
|
||||||
|
throw new IllegalStateException(_channelState.toString());
|
||||||
|
|
||||||
|
_channelState = State.OPEN;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString()
|
public String toString()
|
||||||
{
|
{
|
||||||
return state.get().toString();
|
return _channelState.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public State getState()
|
||||||
|
{
|
||||||
|
synchronized (this)
|
||||||
|
{
|
||||||
|
return _channelState;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isClosed()
|
public boolean isClosed()
|
||||||
{
|
{
|
||||||
State s = state.get();
|
return getState()==State.CLOSED;
|
||||||
return !s.inOpen && !s.outOpen;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isInOpen()
|
public boolean isInOpen()
|
||||||
{
|
{
|
||||||
return state.get().inOpen;
|
State state = getState();
|
||||||
|
return (state==State.OPEN || state==State.OCLOSED);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isOutOpen()
|
public boolean isOutOpen()
|
||||||
{
|
{
|
||||||
return state.get().outOpen;
|
State state = getState();
|
||||||
|
return (state==State.OPEN || state==State.ICLOSED);
|
||||||
}
|
}
|
||||||
|
|
||||||
public CloseStatus getCloseStatus()
|
public CloseStatus getCloseStatus()
|
||||||
{
|
{
|
||||||
return state.get().closeStatus;
|
synchronized (this)
|
||||||
}
|
|
||||||
|
|
||||||
public boolean onCloseIn(CloseStatus closeStatus)
|
|
||||||
{
|
|
||||||
while (true)
|
|
||||||
{
|
{
|
||||||
State s = state.get();
|
return _closeStatus;
|
||||||
|
|
||||||
if (!s.inOpen)
|
|
||||||
throw new IllegalStateException(state.get().toString());
|
|
||||||
|
|
||||||
if (s.outOpen)
|
|
||||||
{
|
|
||||||
State closedIn = new State("ICLOSED", false, true, closeStatus);
|
|
||||||
if (state.compareAndSet(s, closedIn))
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
State closed = new State("CLOSED", false, false, closeStatus);
|
|
||||||
if (state.compareAndSet(s, closed))
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean onCloseOut(CloseStatus closeStatus)
|
|
||||||
{
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
State s = state.get();
|
|
||||||
|
|
||||||
if (!s.outOpen)
|
|
||||||
throw new IllegalStateException(state.get().toString());
|
|
||||||
|
|
||||||
if (s.inOpen)
|
|
||||||
{
|
|
||||||
State closedOut = new State("OCLOSED", true, false, closeStatus);
|
|
||||||
if (state.compareAndSet(s, closedOut))
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
State closed = new State("CLOSED", false, false, closeStatus);
|
|
||||||
if (state.compareAndSet(s, closed))
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean onClosed(CloseStatus closeStatus)
|
public boolean onClosed(CloseStatus closeStatus)
|
||||||
{
|
{
|
||||||
while (true)
|
synchronized (this)
|
||||||
{
|
{
|
||||||
State s = state.get();
|
if (_channelState == State.CLOSED)
|
||||||
if (!s.outOpen && !s.inOpen)
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
State newState = new State("CLOSED", false, false, closeStatus);
|
_closeStatus = closeStatus;
|
||||||
if (state.compareAndSet(s, newState))
|
_channelState = State.CLOSED;
|
||||||
return true;
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean checkOutgoing(Frame frame) throws ProtocolException
|
||||||
|
{
|
||||||
|
byte opcode = frame.getOpCode();
|
||||||
|
boolean fin = frame.isFin();
|
||||||
|
|
||||||
|
synchronized (this)
|
||||||
|
{
|
||||||
|
if (!isOutOpen())
|
||||||
|
throw new IllegalStateException(_channelState.toString());
|
||||||
|
|
||||||
|
if (opcode == OpCode.CLOSE)
|
||||||
|
{
|
||||||
|
_closeStatus = CloseStatus.getCloseStatus(frame);
|
||||||
|
|
||||||
|
switch (_channelState)
|
||||||
|
{
|
||||||
|
case OPEN:
|
||||||
|
_channelState = State.OCLOSED;
|
||||||
|
return false;
|
||||||
|
case ICLOSED:
|
||||||
|
_channelState = State.CLOSED;
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
throw new IllegalStateException(_channelState.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (frame.isDataFrame())
|
||||||
|
{
|
||||||
|
_outgoingContinuation = checkDataSequence(opcode, fin, _outgoingContinuation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean checkIncoming(Frame frame) throws ProtocolException
|
||||||
|
{
|
||||||
|
byte opcode = frame.getOpCode();
|
||||||
|
boolean fin = frame.isFin();
|
||||||
|
|
||||||
|
synchronized (this)
|
||||||
|
{
|
||||||
|
if (!isInOpen())
|
||||||
|
throw new IllegalStateException(_channelState.toString());
|
||||||
|
|
||||||
|
if (opcode == OpCode.CLOSE)
|
||||||
|
{
|
||||||
|
_closeStatus = CloseStatus.getCloseStatus(frame);
|
||||||
|
|
||||||
|
switch (_channelState)
|
||||||
|
{
|
||||||
|
case OPEN:
|
||||||
|
_channelState = State.ICLOSED;
|
||||||
|
return false;
|
||||||
|
case OCLOSED:
|
||||||
|
_channelState = State.CLOSED;
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
throw new IllegalStateException(_channelState.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (frame.isDataFrame())
|
||||||
|
{
|
||||||
|
_incomingContinuation = checkDataSequence(opcode, fin, _incomingContinuation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static byte checkDataSequence(byte opcode, boolean fin, byte lastOpCode) throws ProtocolException
|
||||||
|
{
|
||||||
|
switch (opcode)
|
||||||
|
{
|
||||||
|
case OpCode.TEXT:
|
||||||
|
case OpCode.BINARY:
|
||||||
|
if (lastOpCode != OpCode.UNDEFINED)
|
||||||
|
throw new ProtocolException("DataFrame before fin==true");
|
||||||
|
if (!fin)
|
||||||
|
return opcode;
|
||||||
|
return OpCode.UNDEFINED;
|
||||||
|
|
||||||
|
case OpCode.CONTINUATION:
|
||||||
|
if (lastOpCode == OpCode.UNDEFINED)
|
||||||
|
throw new ProtocolException("CONTINUATION after fin==true");
|
||||||
|
if (fin)
|
||||||
|
return OpCode.UNDEFINED;
|
||||||
|
return lastOpCode;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return lastOpCode;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue