WebSocket core autobahn and MessageHandler refactor (#3802)

* reworked WebSocket autobahn test code and the core MessageHandler

Signed-off-by: Lachlan Roberts <lachlan@webtide.com>

* PR #3802 - changes from review

Signed-off-by: Lachlan Roberts <lachlan@webtide.com>
This commit is contained in:
Lachlan 2019-06-26 13:47:46 +10:00 committed by GitHub
parent 390033edfc
commit cc4304a0f6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 351 additions and 1373 deletions

View File

@ -28,6 +28,7 @@ import java.util.concurrent.BlockingQueue;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.CountDownLatch;
import java.util.concurrent.LinkedBlockingDeque; import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import javax.websocket.ClientEndpointConfig; import javax.websocket.ClientEndpointConfig;
import javax.websocket.CloseReason; import javax.websocket.CloseReason;
import javax.websocket.ContainerProvider; import javax.websocket.ContainerProvider;
@ -343,6 +344,8 @@ public class MessageReceivingTest
public static class ServerMessageNegotiator extends CoreServer.BaseNegotiator public static class ServerMessageNegotiator extends CoreServer.BaseNegotiator
{ {
private static final int MAX_MESSAGE_SIZE = (1024 * 1024) + 2;
public ServerMessageNegotiator() public ServerMessageNegotiator()
{ {
super(); super();
@ -363,7 +366,6 @@ public class MessageReceivingTest
{ {
negotiation.setSubprotocol("partial-binary"); negotiation.setSubprotocol("partial-binary");
SendPartialBinaryFrameHandler frameHandler = new SendPartialBinaryFrameHandler(); SendPartialBinaryFrameHandler frameHandler = new SendPartialBinaryFrameHandler();
frameHandler.setMaxBinaryMessageSize((1024 * 1024) + 2);
return frameHandler; return frameHandler;
} }
@ -371,12 +373,18 @@ public class MessageReceivingTest
{ {
negotiation.setSubprotocol("echo"); negotiation.setSubprotocol("echo");
EchoWholeMessageFrameHandler frameHandler = new EchoWholeMessageFrameHandler(); EchoWholeMessageFrameHandler frameHandler = new EchoWholeMessageFrameHandler();
frameHandler.setMaxTextMessageSize((1024 * 1024) + 2);
return frameHandler; return frameHandler;
} }
return null; return null;
} }
@Override
public void customize(FrameHandler.Configuration configurable)
{
configurable.setMaxBinaryMessageSize(MAX_MESSAGE_SIZE);
configurable.setMaxTextMessageSize(MAX_MESSAGE_SIZE);
}
} }
public static class TestEndpoint extends Endpoint public static class TestEndpoint extends Endpoint

View File

@ -81,11 +81,11 @@ public class LargeAnnotatedTest
client.start(); client.start();
FrameHandlerTracker clientSocket = new FrameHandlerTracker(); FrameHandlerTracker clientSocket = new FrameHandlerTracker();
clientSocket.setMaxTextMessageSize(128 * 1024);
Future<FrameHandler.CoreSession> clientConnectFuture = client.connect(clientSocket, uri.resolve("/app/echo/large")); Future<FrameHandler.CoreSession> clientConnectFuture = client.connect(clientSocket, uri.resolve("/app/echo/large"));
// wait for connect // wait for connect
FrameHandler.CoreSession coreSession = clientConnectFuture.get(1, TimeUnit.SECONDS); FrameHandler.CoreSession coreSession = clientConnectFuture.get(1, TimeUnit.SECONDS);
coreSession.setMaxTextMessageSize(128 * 1024);
try try
{ {

View File

@ -103,11 +103,11 @@ public class LargeContainerTest
client.start(); client.start();
FrameHandlerTracker clientSocket = new FrameHandlerTracker(); FrameHandlerTracker clientSocket = new FrameHandlerTracker();
clientSocket.setMaxTextMessageSize(128 * 1024);
Future<FrameHandler.CoreSession> clientConnectFuture = client.connect(clientSocket, uri.resolve("/app/echo/large")); Future<FrameHandler.CoreSession> clientConnectFuture = client.connect(clientSocket, uri.resolve("/app/echo/large"));
// wait for connect // wait for connect
FrameHandler.CoreSession coreSession = clientConnectFuture.get(5, TimeUnit.SECONDS); FrameHandler.CoreSession coreSession = clientConnectFuture.get(5, TimeUnit.SECONDS);
coreSession.setMaxTextMessageSize(128 * 1024);
try try
{ {
// The message size should be bigger than default, but smaller than the limit that LargeEchoSocket specifies // The message size should be bigger than default, but smaller than the limit that LargeEchoSocket specifies

View File

@ -4,9 +4,7 @@
}, },
"url": "ws://127.0.0.1:9001", "url": "ws://127.0.0.1:9001",
"outdir": "./target/reports/clients", "outdir": "./target/reports/clients",
"cases": [ "cases": ["*"],
"*"
],
"exclude-cases": [], "exclude-cases": [],
"exclude-agent-cases": {} "exclude-agent-cases": {}
} }

View File

@ -324,7 +324,7 @@ public interface FrameHandler extends IncomingFrames
*/ */
void demand(long n); void demand(long n);
class Empty implements CoreSession class Empty extends ConfigurationCustomizer implements CoreSession
{ {
@Override @Override
public String getNegotiatedSubProtocol() public String getNegotiatedSubProtocol()
@ -397,28 +397,6 @@ public interface FrameHandler extends IncomingFrames
return false; return false;
} }
@Override
public Duration getIdleTimeout()
{
return Duration.ZERO;
}
@Override
public Duration getWriteTimeout()
{
return Duration.ZERO;
}
@Override
public void setIdleTimeout(Duration timeout)
{
}
@Override
public void setWriteTimeout(Duration timeout)
{
}
@Override @Override
public void flush(Callback callback) public void flush(Callback callback)
{ {
@ -439,76 +417,10 @@ public interface FrameHandler extends IncomingFrames
{ {
} }
@Override
public boolean isAutoFragment()
{
return false;
}
@Override
public void setAutoFragment(boolean autoFragment)
{
}
@Override
public long getMaxFrameSize()
{
return 0;
}
@Override
public void setMaxFrameSize(long maxFrameSize)
{
}
@Override
public int getOutputBufferSize()
{
return 0;
}
@Override
public void setOutputBufferSize(int outputBufferSize)
{
}
@Override
public int getInputBufferSize()
{
return 0;
}
@Override
public void setInputBufferSize(int inputBufferSize)
{
}
@Override @Override
public void sendFrame(Frame frame, Callback callback, boolean batch) public void sendFrame(Frame frame, Callback callback, boolean batch)
{ {
} }
@Override
public long getMaxBinaryMessageSize()
{
return 0;
}
@Override
public void setMaxBinaryMessageSize(long maxSize)
{
}
@Override
public long getMaxTextMessageSize()
{
return 0;
}
@Override
public void setMaxTextMessageSize(long maxSize)
{
}
} }
} }

View File

@ -18,7 +18,7 @@
package org.eclipse.jetty.websocket.core; package org.eclipse.jetty.websocket.core;
import java.io.IOException; import java.io.ByteArrayOutputStream;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.util.function.Consumer; import java.util.function.Consumer;
@ -39,7 +39,6 @@ import org.eclipse.jetty.util.log.Logger;
*/ */
public class MessageHandler implements FrameHandler public class MessageHandler implements FrameHandler
{ {
public static MessageHandler from(Consumer<String> onText, Consumer<ByteBuffer> onBinary) public static MessageHandler from(Consumer<String> onText, Consumer<ByteBuffer> onBinary)
{ {
return new MessageHandler() return new MessageHandler()
@ -86,56 +85,38 @@ public class MessageHandler implements FrameHandler
}; };
} }
private static final Logger LOG = Log.getLogger(MessageHandler.class); protected static final Logger LOG = Log.getLogger(MessageHandler.class);
private final int factor;
private CoreSession coreSession; private CoreSession coreSession;
private Utf8StringBuilder utf8StringBuilder = null; private Utf8StringBuilder textMessageBuffer;
private ByteBuffer binaryMessage = null; private ByteArrayOutputStream binaryMessageBuffer;
private byte dataType = OpCode.UNDEFINED; private byte dataType = OpCode.UNDEFINED;
private int maxTextMessageSize = WebSocketConstants.DEFAULT_MAX_TEXT_MESSAGE_SIZE;
private int maxBinaryMessageSize = WebSocketConstants.DEFAULT_MAX_BINARY_MESSAGE_SIZE;
public MessageHandler()
{
this(3);
}
public MessageHandler(int factor)
{
this.factor = factor;
}
public int getMaxTextMessageSize()
{
return maxTextMessageSize;
}
public void setMaxTextMessageSize(int maxTextMessageSize)
{
this.maxTextMessageSize = maxTextMessageSize;
}
public int getMaxBinaryMessageSize()
{
return maxBinaryMessageSize;
}
public void setMaxBinaryMessageSize(int maxBinaryMessageSize)
{
this.maxBinaryMessageSize = maxBinaryMessageSize;
}
public CoreSession getCoreSession() public CoreSession getCoreSession()
{ {
return coreSession; return coreSession;
} }
private Utf8StringBuilder getTextMessageBuffer()
{
if (textMessageBuffer == null)
textMessageBuffer = new Utf8StringBuilder();
return textMessageBuffer;
}
private ByteArrayOutputStream getBinaryMessageBuffer()
{
if (binaryMessageBuffer == null)
binaryMessageBuffer = new ByteArrayOutputStream();
return binaryMessageBuffer;
}
@Override @Override
public void onOpen(CoreSession coreSession, Callback callback) public void onOpen(CoreSession coreSession, Callback callback)
{ {
if (LOG.isDebugEnabled())
LOG.debug("onOpen {}", coreSession);
this.coreSession = coreSession; this.coreSession = coreSession;
callback.succeeded(); callback.succeeded();
} }
@ -143,90 +124,33 @@ public class MessageHandler implements FrameHandler
@Override @Override
public void onFrame(Frame frame, Callback callback) public void onFrame(Frame frame, Callback callback)
{ {
try if (LOG.isDebugEnabled())
LOG.debug("onFrame {}", frame);
switch (frame.getOpCode())
{ {
byte opcode = frame.getOpCode(); case OpCode.CLOSE:
if (LOG.isDebugEnabled()) onCloseFrame(frame, callback);
LOG.debug("{}: {}", OpCode.name(opcode), BufferUtil.toDetailString(frame.getPayload())); break;
case OpCode.PING:
switch (opcode) onPingFrame(frame, callback);
{ break;
case OpCode.PING: case OpCode.PONG:
case OpCode.PONG: onPongFrame(frame, callback);
case OpCode.CLOSE: break;
if (isDemanding()) case OpCode.TEXT:
getCoreSession().demand(1); dataType = OpCode.TEXT;
callback.succeeded(); onTextFrame(frame, callback);
break; break;
case OpCode.BINARY:
case OpCode.BINARY: dataType = OpCode.BINARY;
if (frame.isFin()) onBinaryFrame(frame, callback);
{ break;
final int maxSize = getMaxBinaryMessageSize(); case OpCode.CONTINUATION:
if (frame.hasPayload() && frame.getPayload().remaining() > maxSize) onContinuationFrame(frame, callback);
throw new MessageTooLargeException("Message larger than " + maxSize + " bytes"); if (frame.isFin())
dataType = OpCode.UNDEFINED;
onBinary(frame.getPayload(), callback); //bypass buffer aggregation break;
}
else
{
dataType = OpCode.BINARY;
binaryMessage = getCoreSession().getByteBufferPool().acquire(frame.getPayloadLength() * factor, false);
onBinaryFrame(frame, callback);
}
break;
case OpCode.TEXT:
dataType = OpCode.TEXT;
if (utf8StringBuilder == null)
{
final int maxSize = getMaxTextMessageSize();
utf8StringBuilder = (maxSize < 0) ? new Utf8StringBuilder() : new Utf8StringBuilder()
{
@Override
protected void appendByte(byte b) throws IOException
{
// TODO can we avoid byte by byte length check?
if (length() >= maxSize)
throw new MessageTooLargeException("Message larger than " + maxSize + " characters");
super.appendByte(b);
}
};
}
onTextFrame(frame, callback);
break;
case OpCode.CONTINUATION:
switch (dataType)
{
case OpCode.BINARY:
onBinaryFrame(frame, callback);
break;
case OpCode.TEXT:
onTextFrame(frame, callback);
break;
default:
throw new IllegalStateException();
}
break;
default:
throw new IllegalStateException();
}
}
catch (Utf8Appendable.NotUtf8Exception bple)
{
utf8StringBuilder.reset();
callback.failed(new BadPayloadException(bple));
}
catch (Throwable th)
{
if (utf8StringBuilder != null)
utf8StringBuilder.reset();
callback.failed(th);
} }
} }
@ -234,7 +158,8 @@ public class MessageHandler implements FrameHandler
public void onError(Throwable cause, Callback callback) public void onError(Throwable cause, Callback callback)
{ {
if (LOG.isDebugEnabled()) if (LOG.isDebugEnabled())
LOG.debug(this + " onError ", cause); LOG.debug("onError ", cause);
callback.succeeded(); callback.succeeded();
} }
@ -242,98 +167,128 @@ public class MessageHandler implements FrameHandler
public void onClosed(CloseStatus closeStatus, Callback callback) public void onClosed(CloseStatus closeStatus, Callback callback)
{ {
if (LOG.isDebugEnabled()) if (LOG.isDebugEnabled())
LOG.debug("{} onClosed {}", this, closeStatus); LOG.debug("onClosed {}", closeStatus);
if (utf8StringBuilder != null && utf8StringBuilder.length() > 0 && closeStatus.isNormal())
LOG.warn("{} closed with partial message: {} chars", utf8StringBuilder.length());
if (binaryMessage != null) if (textMessageBuffer != null)
{ {
if (BufferUtil.hasContent(binaryMessage)) textMessageBuffer.reset();
LOG.warn("{} closed with partial message: {} bytes", binaryMessage.remaining()); textMessageBuffer = null;
getCoreSession().getByteBufferPool().release(binaryMessage);
binaryMessage = null;
} }
if (utf8StringBuilder != null) if (binaryMessageBuffer != null)
{ {
utf8StringBuilder.reset(); binaryMessageBuffer.reset();
utf8StringBuilder = null; binaryMessageBuffer = null;
} }
coreSession = null;
callback.succeeded(); callback.succeeded();
} }
private void onTextFrame(Frame frame, Callback callback) protected void onTextFrame(Frame frame, Callback callback)
{ {
if (frame.hasPayload()) try
utf8StringBuilder.append(frame.getPayload());
if (frame.isFin())
{ {
dataType = OpCode.UNDEFINED; Utf8StringBuilder textBuffer = getTextMessageBuffer();
String message = utf8StringBuilder.toString(); if (frame.hasPayload())
utf8StringBuilder.reset(); {
onText(message, callback); long maxSize = coreSession.getMaxTextMessageSize();
long currentSize = frame.getPayload().remaining() + textBuffer.length();
if (currentSize > maxSize)
throw new MessageTooLargeException("Message larger than " + maxSize + " bytes");
textBuffer.append(frame.getPayload());
}
if (frame.isFin())
{
onText(textBuffer.toString(), callback);
textBuffer.reset();
}
else
{
callback.succeeded();
}
} }
else catch (Utf8Appendable.NotUtf8Exception e)
{ {
if (isDemanding()) callback.failed(new BadPayloadException(e));
getCoreSession().demand(1); }
callback.succeeded(); catch (Throwable t)
{
callback.failed(t);
}
}
protected void onBinaryFrame(Frame frame, Callback callback)
{
try
{
ByteArrayOutputStream binaryBuffer = getBinaryMessageBuffer();
if (frame.hasPayload())
{
long maxSize = coreSession.getMaxBinaryMessageSize();
long currentSize = frame.getPayload().remaining() + binaryBuffer.size();
if (currentSize > maxSize)
throw new MessageTooLargeException("Message larger than " + maxSize + " bytes");
BufferUtil.writeTo(frame.getPayload(), binaryBuffer);
}
if (frame.isFin())
{
onBinary(BufferUtil.toBuffer(binaryBuffer.toByteArray()), callback);
binaryBuffer.reset();
}
else
{
callback.succeeded();
}
}
catch (Throwable t)
{
callback.failed(t);
} }
} }
private void onBinaryFrame(Frame frame, Callback callback) protected void onContinuationFrame(Frame frame, Callback callback)
{ {
if (frame.hasPayload()) switch (dataType)
{ {
if (BufferUtil.space(binaryMessage) < frame.getPayloadLength()) case OpCode.BINARY:
binaryMessage = BufferUtil onBinaryFrame(frame, callback);
.ensureCapacity(binaryMessage, binaryMessage.capacity() + Math.max(binaryMessage.capacity(), frame.getPayloadLength() * factor)); break;
BufferUtil.append(binaryMessage, frame.getPayload()); case OpCode.TEXT:
onTextFrame(frame, callback);
break;
default:
throw new IllegalStateException();
} }
}
final int maxSize = getMaxBinaryMessageSize(); protected void onPingFrame(Frame frame, Callback callback)
if (binaryMessage.remaining() > maxSize) {
{ coreSession.sendFrame(new Frame(OpCode.PONG, true, frame.getPayload()), callback, false);
getCoreSession().getByteBufferPool().release(binaryMessage); }
binaryMessage = null;
throw new MessageTooLargeException("Message larger than " + maxSize + " bytes");
}
if (frame.isFin()) protected void onPongFrame(Frame frame, Callback callback)
{ {
dataType = OpCode.UNDEFINED; callback.succeeded();
}
final ByteBuffer completeMessage = binaryMessage; protected void onCloseFrame(Frame frame, Callback callback)
binaryMessage = null; {
callback.succeeded();
callback = new Callback.Nested(callback)
{
@Override
public void completed()
{
getCoreSession().getByteBufferPool().release(completeMessage);
}
};
onBinary(completeMessage, callback);
}
else
{
if (isDemanding())
getCoreSession().demand(1);
callback.succeeded();
}
} }
/** /**
* Method called when a complete text message is received. * Method called when a complete text message is received.
* *
* @param message the received text payload * @param message the received text payload
* @param callback The callback to signal completion of handling. * @param callback The callback to signal completion of handling.
*/ */
protected void onText(String message, Callback callback) protected void onText(String message, Callback callback)
@ -344,7 +299,7 @@ public class MessageHandler implements FrameHandler
/** /**
* Method called when a complete binary message is received. * Method called when a complete binary message is received.
* *
* @param message The binary payload * @param message The binary payload
* @param callback The callback to signal completion of handling. * @param callback The callback to signal completion of handling.
*/ */
protected void onBinary(ByteBuffer message, Callback callback) protected void onBinary(ByteBuffer message, Callback callback)
@ -355,13 +310,12 @@ public class MessageHandler implements FrameHandler
/** /**
* Send a String as a single text frame. * Send a String as a single text frame.
* *
* @param message The message to send * @param message The message to send
* @param callback The callback to call when the send is complete * @param callback The callback to call when the send is complete
* @param batch The batch mode to send the frames in. * @param batch The batch mode to send the frames in.
*/ */
public void sendText(String message, Callback callback, boolean batch) public void sendText(String message, Callback callback, boolean batch)
{ {
// TODO if autofragment is on, enforce max buffer size
getCoreSession().sendFrame(new Frame(OpCode.TEXT, true, message), callback, batch); getCoreSession().sendFrame(new Frame(OpCode.TEXT, true, message), callback, batch);
} }
@ -371,8 +325,8 @@ public class MessageHandler implements FrameHandler
* single fragment need be converted to bytes * single fragment need be converted to bytes
* *
* @param callback The callback to call when the send is complete * @param callback The callback to call when the send is complete
* @param batch The batch mode to send the frames in. * @param batch The batch mode to send the frames in.
* @param parts The parts of the message. * @param parts The parts of the message.
*/ */
public void sendText(Callback callback, boolean batch, final String... parts) public void sendText(Callback callback, boolean batch, final String... parts)
{ {
@ -400,8 +354,8 @@ public class MessageHandler implements FrameHandler
String part = parts[i++]; String part = parts[i++];
getCoreSession().sendFrame(new Frame( getCoreSession().sendFrame(new Frame(
i == 1 ? OpCode.TEXT : OpCode.CONTINUATION, i == 1 ? OpCode.TEXT : OpCode.CONTINUATION,
i == parts.length, part), this, batch); i == parts.length, part), this, batch);
return Action.SCHEDULED; return Action.SCHEDULED;
} }
}.iterate(); }.iterate();
@ -410,9 +364,9 @@ public class MessageHandler implements FrameHandler
/** /**
* Send a ByteBuffer as a single binary frame. * Send a ByteBuffer as a single binary frame.
* *
* @param message The message to send * @param message The message to send
* @param callback The callback to call when the send is complete * @param callback The callback to call when the send is complete
* @param batch The batch mode to send the frames in. * @param batch The batch mode to send the frames in.
*/ */
public void sendBinary(ByteBuffer message, Callback callback, boolean batch) public void sendBinary(ByteBuffer message, Callback callback, boolean batch)
{ {
@ -423,8 +377,8 @@ public class MessageHandler implements FrameHandler
* Send a sequence of ByteBuffers as a sequences for fragmented text frame. * Send a sequence of ByteBuffers as a sequences for fragmented text frame.
* *
* @param callback The callback to call when the send is complete * @param callback The callback to call when the send is complete
* @param batch The batch mode to send the frames in. * @param batch The batch mode to send the frames in.
* @param parts The parts of the message. * @param parts The parts of the message.
*/ */
public void sendBinary(Callback callback, boolean batch, final ByteBuffer... parts) public void sendBinary(Callback callback, boolean batch, final ByteBuffer... parts)
{ {
@ -452,8 +406,8 @@ public class MessageHandler implements FrameHandler
ByteBuffer part = parts[i++]; ByteBuffer part = parts[i++];
getCoreSession().sendFrame(new Frame( getCoreSession().sendFrame(new Frame(
i == 1 ? OpCode.BINARY : OpCode.CONTINUATION, i == 1 ? OpCode.BINARY : OpCode.CONTINUATION,
i == parts.length, part), this, batch); i == parts.length, part), this, batch);
return Action.SCHEDULED; return Action.SCHEDULED;
} }
}.iterate(); }.iterate();

View File

@ -1,349 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995-2019 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.websocket.core;
import java.nio.ByteBuffer;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.Utf8StringBuilder;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import static org.eclipse.jetty.websocket.core.OpCode.PONG;
/**
* Base level implementation of local WebSocket Endpoint Frame handling.
* <p>
* This implementation assumes RFC6455 behavior with HTTP/1.1.
* NOTE: The introduction of WebSocket over HTTP/2 might change the behavior and implementation some.
* </p>
*/
public class AbstractTestFrameHandler implements SynchronousFrameHandler
{
private Logger LOG = Log.getLogger(AbstractTestFrameHandler.class);
private byte partial = OpCode.UNDEFINED;
private Utf8StringBuilder utf8;
private ByteBuffer byteBuffer;
private FrameHandler.CoreSession coreSession;
public FrameHandler.CoreSession getCoreSession()
{
return coreSession;
}
@Override
public void onOpen(CoreSession coreSession)
{
this.coreSession = coreSession;
onOpen();
}
public void onOpen()
{
}
@Override
public void onFrame(Frame frame, Callback callback)
{
byte opcode = frame.getOpCode();
if (LOG.isDebugEnabled())
LOG.debug("{}: {}", OpCode.name(opcode), BufferUtil.toDetailString(frame.getPayload()));
switch (opcode)
{
case OpCode.PING:
onPingFrame(frame, callback);
break;
case OpCode.PONG:
onPongFrame(frame, callback);
break;
case OpCode.TEXT:
onTextFrame(frame, callback);
break;
case OpCode.BINARY:
onBinaryFrame(frame, callback);
break;
case OpCode.CONTINUATION:
onContinuationFrame(frame, callback);
break;
case OpCode.CLOSE:
onCloseFrame(frame, callback);
break;
}
}
@Override
public void onError(Throwable cause)
{
}
/**
* Notification method for when a Ping frame is received.
* The default implementation sends a Pong frame using the passed callback for completion
*
* @param frame The received frame
* @param callback The callback to indicate completion of frame handling.
*/
protected void onPingFrame(Frame frame, Callback callback)
{
ByteBuffer pongBuf;
if (frame.hasPayload())
{
pongBuf = ByteBuffer.allocate(frame.getPayload().remaining());
BufferUtil.put(frame.getPayload().slice(), pongBuf);
BufferUtil.flipToFlush(pongBuf, 0);
}
else
{
pongBuf = ByteBuffer.allocate(0);
}
try
{
coreSession.sendFrame(new Frame(PONG).setPayload(pongBuf), callback, false);
}
catch (Throwable t)
{
if (LOG.isDebugEnabled())
LOG.debug("Unable to send pong", t);
callback.failed(t);
}
}
/**
* Notification method for when a Pong frame is received.
* The default implementation just succeeds the callback
*
* @param frame The received frame
* @param callback The callback to indicate completion of frame handling.
*/
protected void onPongFrame(Frame frame, Callback callback)
{
callback.succeeded();
}
/**
* Notification method for when a Text frame is received.
* The default implementation accumulates the payload in a Utf8StringBuilder
* and calls the {@link #onText(Utf8StringBuilder, Callback, boolean)} method.
* For partial textMessages (fin == false), the {@link #onText(Utf8StringBuilder, Callback, boolean)}
* may either leave the contents in the Utf8StringBuilder to accumulate with following Continuation
* frames, or it may be consumed.
*
* @param frame The received frame
* @param callback The callback to indicate completion of frame handling.
* @see #onText(Utf8StringBuilder, Callback, boolean)
*/
protected void onTextFrame(Frame frame, Callback callback)
{
if (utf8 == null)
utf8 = new Utf8StringBuilder(Math.max(1024, frame.getPayloadLength() * 2));
else
utf8.reset();
if (frame.hasPayload())
utf8.append(frame
.getPayload()); // TODO: this should trigger a bad UTF8 exception if sequence is bad which we wrap in a ProtocolException (but not on unfinished sequences)
if (frame.isFin())
utf8.checkState(); // TODO: this should not be necessary, checkState() shouldn't be necessary to use (the utf8.toString() should trigger on bad utf8 in final octets)
else
partial = OpCode.TEXT;
onText(utf8, callback, frame.isFin());
}
/**
* Notification method for when UTF8 text is received. This method is
* called by {@link #onTextFrame(Frame, Callback)} and
* {@link #onContinuationFrame(Frame, Callback)}. Implementations
* may consume partial content with {@link Utf8StringBuilder#takePartialString()}
* or leave it to accumulate over multiple calls.
* The default implementation just succeeds the callback.
*
* @param utf8 The received text
* @param callback The callback to indicate completion of frame handling.
* @param fin True if the current message is completed by this call.
*/
protected void onText(Utf8StringBuilder utf8, Callback callback, boolean fin)
{
callback.succeeded();
}
/**
* Notification method for when a Binary frame is received.
* The default implementation accumulates the payload in a ByteBuffer
* and calls the {@link #onBinary(ByteBuffer, Callback, boolean)} method.
* For partial textMessages (fin == false), the {@link #onBinary(ByteBuffer, Callback, boolean)}
* may either leave the contents in the ByteBuffer to accumulate with following Continuation
* frames, or it may be consumed.
*
* @param frame The received frame
* @param callback The callback to indicate completion of frame handling.
* @see #onBinary(ByteBuffer, Callback, boolean)
*/
protected void onBinaryFrame(Frame frame, Callback callback)
{
if (frame.isFin())
{
onBinary(frame.getPayload(), callback, true);
}
else
{
partial = OpCode.BINARY;
// TODO use the pool?
if (byteBuffer == null)
byteBuffer = BufferUtil.allocate(Math.max(1024, frame.getPayloadLength() * 2));
else
BufferUtil.clear(byteBuffer);
if (frame.hasPayload())
BufferUtil.append(byteBuffer, frame.getPayload());
onBinary(byteBuffer, callback, false);
}
}
/**
* Notification method for when binary data is received. This method is
* called by {@link #onBinaryFrame(Frame, Callback)} and
* {@link #onContinuationFrame(Frame, Callback)}. Implementations
* may consume partial content from the {@link ByteBuffer}
* or leave it to accumulate over multiple calls.
* The default implementation just succeeds the callback.
*
* @param payload The received data
* @param callback The callback to indicate completion of frame handling.
* @param fin True if the current message is completed by this call.
*/
protected void onBinary(ByteBuffer payload, Callback callback, boolean fin)
{
callback.succeeded();
}
/**
* Notification method for when a Continuation frame is received.
* The default implementation will call either {@link #onText(Utf8StringBuilder, Callback, boolean)}
* or {@link #onBinary(ByteBuffer, Callback, boolean)} as appropriate, accumulating
* payload as necessary.
*
* @param frame The received frame
* @param callback The callback to indicate completion of frame handling.
*/
protected void onContinuationFrame(Frame frame, Callback callback)
{
if (partial == OpCode.UNDEFINED)
{
callback.failed(new IllegalStateException());
return;
}
switch (partial)
{
case OpCode.TEXT:
if (frame.hasPayload())
utf8.append(frame.getPayload());
if (frame.isFin())
utf8.checkState();
onText(utf8, callback, frame.isFin());
break;
case OpCode.BINARY:
if (frame.hasPayload())
{
int factor = frame.isFin() ? 1 : 3;
BufferUtil.compact(byteBuffer);
if (BufferUtil.space(byteBuffer) < frame.getPayloadLength())
byteBuffer = BufferUtil
.ensureCapacity(byteBuffer, byteBuffer.capacity() + Math.max(byteBuffer.capacity(), frame.getPayloadLength() * factor));
BufferUtil.append(byteBuffer, frame.getPayload());
}
onBinary(byteBuffer, callback, frame.isFin());
break;
default:
callback.failed(new IllegalStateException());
}
}
/**
* Notification method for when a Close frame is received.
* The default implementation responds with a close frame when necessary.
*
* @param frame The received frame
* @param callback The callback to indicate completion of frame handling.
*/
protected void onCloseFrame(Frame frame, Callback callback)
{
int respond;
String reason = null;
int code = frame.hasPayload() ? new CloseStatus(frame.getPayload()).getCode() : -1;
switch (code)
{
case -1:
respond = CloseStatus.NORMAL;
break;
case CloseStatus.NORMAL:
case CloseStatus.SHUTDOWN:
case CloseStatus.PROTOCOL:
case CloseStatus.BAD_DATA:
case CloseStatus.BAD_PAYLOAD:
case CloseStatus.POLICY_VIOLATION:
case CloseStatus.MESSAGE_TOO_LARGE:
case CloseStatus.EXTENSION_ERROR:
case CloseStatus.SERVER_ERROR:
respond = 0;
break;
default:
if (code >= 3000 && code <= 4999)
{
respond = code;
}
else
{
respond = CloseStatus.PROTOCOL;
reason = "invalid " + code + " close received";
}
break;
}
if (respond > 0)
coreSession.close(respond, reason, callback);
else
callback.succeeded();
}
@Override
public void onClosed(CloseStatus closeStatus)
{
}
}

View File

@ -65,7 +65,7 @@ public class GeneratorFrameFlagsTest
{ {
ExtensionStack exStack = new ExtensionStack(new WebSocketExtensionRegistry(), Behavior.SERVER); ExtensionStack exStack = new ExtensionStack(new WebSocketExtensionRegistry(), Behavior.SERVER);
exStack.negotiate(new DecoratedObjectFactory(), bufferPool, new LinkedList<>(), new LinkedList<>()); exStack.negotiate(new DecoratedObjectFactory(), bufferPool, new LinkedList<>(), new LinkedList<>());
this.coreSession = new WebSocketCoreSession(new AbstractTestFrameHandler(), Behavior.CLIENT, Negotiated.from(exStack)); this.coreSession = new WebSocketCoreSession(new TestMessageHandler(), Behavior.CLIENT, Negotiated.from(exStack));
} }
@ParameterizedTest @ParameterizedTest

View File

@ -56,7 +56,7 @@ public class GeneratorTest
ByteBufferPool bufferPool = new MappedByteBufferPool(); ByteBufferPool bufferPool = new MappedByteBufferPool();
ExtensionStack exStack = new ExtensionStack(new WebSocketExtensionRegistry(), Behavior.SERVER); ExtensionStack exStack = new ExtensionStack(new WebSocketExtensionRegistry(), Behavior.SERVER);
exStack.negotiate(new DecoratedObjectFactory(), bufferPool, new LinkedList<>(), new LinkedList<>()); exStack.negotiate(new DecoratedObjectFactory(), bufferPool, new LinkedList<>(), new LinkedList<>());
return new WebSocketCoreSession(new AbstractTestFrameHandler(), behavior, Negotiated.from(exStack)); return new WebSocketCoreSession(new TestMessageHandler(), behavior, Negotiated.from(exStack));
} }
/** /**

View File

@ -49,7 +49,6 @@ public class MessageHandlerTest
static byte[] nonUtf8Bytes = {0x7F, (byte)0xFF, (byte)0xFF}; static byte[] nonUtf8Bytes = {0x7F, (byte)0xFF, (byte)0xFF};
boolean demanding; boolean demanding;
int demand;
CoreSession coreSession; CoreSession coreSession;
List<String> textMessages = new ArrayList<>(); List<String> textMessages = new ArrayList<>();
List<ByteBuffer> binaryMessages = new ArrayList<>(); List<ByteBuffer> binaryMessages = new ArrayList<>();
@ -61,7 +60,6 @@ public class MessageHandlerTest
public void beforeEach() throws Exception public void beforeEach() throws Exception
{ {
demanding = false; demanding = false;
demand = 0;
coreSession = new CoreSession.Empty() coreSession = new CoreSession.Empty()
{ {
@ -79,12 +77,6 @@ public class MessageHandlerTest
{ {
return byteBufferPool; return byteBufferPool;
} }
@Override
public void demand(long n)
{
demand += n;
}
}; };
handler = new MessageHandler() handler = new MessageHandler()
@ -127,7 +119,7 @@ public class MessageHandlerTest
assertThat(callback.isDone(), is(true)); assertThat(callback.isDone(), is(true));
assertThat(textMessages.size(), is(0)); assertThat(textMessages.size(), is(0));
assertThat(frames.size(), is(0)); assertThat(frames.size(), is(1));
} }
@Test @Test
@ -276,61 +268,12 @@ public class MessageHandlerTest
assertThat(frames.size(), is(0)); assertThat(frames.size(), is(0));
} }
@Test
public void testDemanding()
{
demanding = true;
FutureCallback callback;
callback = new FutureCallback();
handler.onFrame(new Frame(OpCode.PING, true, "test"), callback);
assertThat(callback.isDone(), is(true));
assertThat(demand, is(1));
callback = new FutureCallback();
handler.onFrame(new Frame(OpCode.TEXT, true, "test"), callback);
assertThat(callback.isDone(), is(false));
assertThat(textMessages.size(), is(1));
assertThat(textMessages.get(0), is("test"));
assertThat(callbacks.size(), is(1));
callbacks.get(0).succeeded();
assertThat(demand, is(1));
callback = new FutureCallback();
handler.onFrame(new Frame(OpCode.TEXT, false, "Hello"), callback);
assertThat(callback.isDone(), is(true));
assertThat(textMessages.size(), is(1));
assertThat(callbacks.size(), is(1));
assertThat(demand, is(2));
callback = new FutureCallback();
handler.onFrame(new Frame(OpCode.CONTINUATION, false, " "), callback);
assertThat(callback.isDone(), is(true));
assertThat(textMessages.size(), is(1));
assertThat(callbacks.size(), is(1));
assertThat(demand, is(3));
callback = new FutureCallback();
handler.onFrame(new Frame(OpCode.CONTINUATION, true, "World"), callback);
assertThat(callback.isDone(), is(false));
assertThat(textMessages.size(), is(2));
assertThat(textMessages.get(1), is("Hello World"));
assertThat(callbacks.size(), is(2));
callbacks.get(1).succeeded();
assertThat(callback.isDone(), is(true));
FutureCallback finalCallback = callback;
assertDoesNotThrow(() -> finalCallback.get());
assertThat(demand, is(3));
assertThat(frames.size(), is(0));
}
@Test @Test
public void testTextNotTooLarge() public void testTextNotTooLarge()
{ {
FutureCallback callback; FutureCallback callback;
handler.setMaxTextMessageSize(4); coreSession.setMaxTextMessageSize(4);
callback = new FutureCallback(); callback = new FutureCallback();
handler.onFrame(new Frame(OpCode.TEXT, true, "test"), callback); handler.onFrame(new Frame(OpCode.TEXT, true, "test"), callback);
@ -348,7 +291,7 @@ public class MessageHandlerTest
{ {
FutureCallback callback; FutureCallback callback;
handler.setMaxTextMessageSize(4); coreSession.setMaxTextMessageSize(4);
handler.onOpen(coreSession, NOOP); handler.onOpen(coreSession, NOOP);
callback = new FutureCallback(); callback = new FutureCallback();
@ -367,7 +310,7 @@ public class MessageHandlerTest
{ {
FutureCallback callback; FutureCallback callback;
handler.setMaxTextMessageSize(4); coreSession.setMaxTextMessageSize(4);
handler.onOpen(coreSession, NOOP); handler.onOpen(coreSession, NOOP);
callback = new FutureCallback(); callback = new FutureCallback();
@ -392,28 +335,21 @@ public class MessageHandlerTest
@Test @Test
public void testLargeBytesSmallCharsTooLarge() public void testLargeBytesSmallCharsTooLarge()
{ {
FutureCallback callback; coreSession.setMaxTextMessageSize(4);
handler.setMaxTextMessageSize(4); FutureCallback callback1 = new FutureCallback();
handler.onFrame(new Frame(OpCode.TEXT, false, BufferUtil.toBuffer(fourByteUtf8Bytes)), callback1);
callback = new FutureCallback(); assertThat(callback1.isDone(), is(true));
handler.onFrame(new Frame(OpCode.TEXT, false, BufferUtil.toBuffer(fourByteUtf8Bytes)), callback);
assertThat(callback.isDone(), is(true));
assertThat(textMessages.size(), is(0)); assertThat(textMessages.size(), is(0));
assertThat(callbacks.size(), is(0)); assertThat(callbacks.size(), is(0));
FutureCallback finalCallback = callback; assertDoesNotThrow(() -> callback1.get());
assertDoesNotThrow(() -> finalCallback.get()); FutureCallback callback2 = new FutureCallback();
handler.onFrame(new Frame(OpCode.TEXT, true, BufferUtil.toBuffer(fourByteUtf8Bytes)), callback2);
callback = new FutureCallback(); assertThat(callback2.isDone(), is(true));
handler.onFrame(new Frame(OpCode.TEXT, true, BufferUtil.toBuffer(fourByteUtf8Bytes)), callback); assertThat(textMessages.size(), is(0));
assertThat(callback.isDone(), is(false)); assertThat(callbacks.size(), is(0));
assertThat(textMessages.size(), is(1)); ExecutionException e = assertThrows(ExecutionException.class, () -> callback2.get());
assertThat(textMessages.get(0), is(fourByteUtf8String + fourByteUtf8String)); assertThat(e.getCause(), instanceOf(MessageTooLargeException.class));
assertThat(callbacks.size(), is(1));
callbacks.get(0).succeeded();
assertThat(callback.isDone(), is(true));
FutureCallback finalCallback1 = callback;
assertDoesNotThrow(() -> finalCallback1.get());
} }
@Test @Test
@ -495,61 +431,12 @@ public class MessageHandlerTest
assertThat(frames.size(), is(0)); assertThat(frames.size(), is(0));
} }
@Test
public void testDemandingBinary()
{
demanding = true;
FutureCallback callback;
callback = new FutureCallback();
handler.onFrame(new Frame(OpCode.PING, true, "test"), callback);
assertThat(callback.isDone(), is(true));
assertThat(demand, is(1));
callback = new FutureCallback();
handler.onFrame(new Frame(OpCode.BINARY, true, "test"), callback);
assertThat(callback.isDone(), is(false));
assertThat(binaryMessages.size(), is(1));
assertThat(BufferUtil.toString(binaryMessages.get(0)), is("test"));
assertThat(callbacks.size(), is(1));
callbacks.get(0).succeeded();
assertThat(demand, is(1));
callback = new FutureCallback();
handler.onFrame(new Frame(OpCode.BINARY, false, "Hello"), callback);
assertThat(callback.isDone(), is(true));
assertThat(binaryMessages.size(), is(1));
assertThat(callbacks.size(), is(1));
assertThat(demand, is(2));
callback = new FutureCallback();
handler.onFrame(new Frame(OpCode.CONTINUATION, false, " "), callback);
assertThat(callback.isDone(), is(true));
assertThat(binaryMessages.size(), is(1));
assertThat(callbacks.size(), is(1));
assertThat(demand, is(3));
callback = new FutureCallback();
handler.onFrame(new Frame(OpCode.CONTINUATION, true, "World"), callback);
assertThat(callback.isDone(), is(false));
assertThat(binaryMessages.size(), is(2));
assertThat(BufferUtil.toString(binaryMessages.get(1)), is("Hello World"));
assertThat(callbacks.size(), is(2));
callbacks.get(1).succeeded();
assertThat(callback.isDone(), is(true));
FutureCallback finalCallback = callback;
assertDoesNotThrow(() -> finalCallback.get());
assertThat(demand, is(3));
assertThat(frames.size(), is(0));
}
@Test @Test
public void testBinaryNotTooLarge() public void testBinaryNotTooLarge()
{ {
FutureCallback callback; FutureCallback callback;
handler.setMaxBinaryMessageSize(4); coreSession.setMaxBinaryMessageSize(4);
callback = new FutureCallback(); callback = new FutureCallback();
handler.onFrame(new Frame(OpCode.BINARY, true, "test"), callback); handler.onFrame(new Frame(OpCode.BINARY, true, "test"), callback);
@ -567,7 +454,7 @@ public class MessageHandlerTest
{ {
FutureCallback callback; FutureCallback callback;
handler.setMaxBinaryMessageSize(4); coreSession.setMaxBinaryMessageSize(4);
handler.onOpen(coreSession, NOOP); handler.onOpen(coreSession, NOOP);
callback = new FutureCallback(); callback = new FutureCallback();
@ -586,7 +473,7 @@ public class MessageHandlerTest
{ {
FutureCallback callback; FutureCallback callback;
handler.setMaxBinaryMessageSize(4); coreSession.setMaxBinaryMessageSize(4);
handler.onOpen(coreSession, NOOP); handler.onOpen(coreSession, NOOP);
callback = new FutureCallback(); callback = new FutureCallback();

View File

@ -59,7 +59,7 @@ public class ParserCapture
ByteBufferPool bufferPool = new MappedByteBufferPool(); ByteBufferPool bufferPool = new MappedByteBufferPool();
ExtensionStack exStack = new ExtensionStack(new WebSocketExtensionRegistry(), Behavior.SERVER); ExtensionStack exStack = new ExtensionStack(new WebSocketExtensionRegistry(), Behavior.SERVER);
exStack.negotiate(new DecoratedObjectFactory(), bufferPool, new LinkedList<>(), new LinkedList<>()); exStack.negotiate(new DecoratedObjectFactory(), bufferPool, new LinkedList<>(), new LinkedList<>());
this.coreSession = new WebSocketCoreSession(new AbstractTestFrameHandler(), behavior, Negotiated.from(exStack)); this.coreSession = new WebSocketCoreSession(new TestMessageHandler(), behavior, Negotiated.from(exStack));
this.parser = new Parser(bufferPool, coreSession); this.parser = new Parser(bufferPool, coreSession);
} }

View File

@ -0,0 +1,94 @@
//
// ========================================================================
// Copyright (c) 1995-2019 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.websocket.core;
import java.nio.ByteBuffer;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.CountDownLatch;
import org.eclipse.jetty.util.BlockingArrayQueue;
import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
public class TestMessageHandler extends MessageHandler
{
protected final Logger LOG = Log.getLogger(TestMessageHandler.class);
public CoreSession coreSession;
public BlockingQueue<String> textMessages = new BlockingArrayQueue<>();
public BlockingQueue<ByteBuffer> binaryMessages = new BlockingArrayQueue<>();
public volatile Throwable error;
public CountDownLatch openLatch = new CountDownLatch(1);
public CountDownLatch errorLatch = new CountDownLatch(1);
public CountDownLatch closeLatch = new CountDownLatch(1);
@Override
public void onOpen(CoreSession coreSession, Callback callback)
{
if (LOG.isDebugEnabled())
LOG.debug("onOpen {}", coreSession);
this.coreSession = coreSession;
super.onOpen(coreSession, callback);
openLatch.countDown();
}
@Override
public void onFrame(Frame frame, Callback callback)
{
if (LOG.isDebugEnabled())
LOG.debug("onFrame {}", frame);
super.onFrame(frame, callback);
}
@Override
public void onError(Throwable cause, Callback callback)
{
if (LOG.isDebugEnabled())
LOG.debug("onError {}", cause);
super.onError(cause, callback);
error = cause;
errorLatch.countDown();
}
@Override
public void onClosed(CloseStatus closeStatus, Callback callback)
{
if (LOG.isDebugEnabled())
LOG.debug("onClosed {}", closeStatus);
super.onClosed(closeStatus, callback);
closeLatch.countDown();
}
@Override
protected void onText(String message, Callback callback)
{
if (LOG.isDebugEnabled())
LOG.debug("onText {}", message);
textMessages.offer(message);
}
@Override
protected void onBinary(ByteBuffer message, Callback callback)
{
if (LOG.isDebugEnabled())
LOG.debug("onBinary {}", message);
binaryMessages.offer(message);
}
}

View File

@ -14,89 +14,38 @@
// //
// You may elect to redistribute this code under either of these licenses. // You may elect to redistribute this code under either of these licenses.
// ======================================================================== // ========================================================================
// //
package org.eclipse.jetty.websocket.core.autobahn; package org.eclipse.jetty.websocket.core.autobahn;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.util.concurrent.atomic.AtomicBoolean; import java.time.Duration;
import org.eclipse.jetty.util.Callback; import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.Utf8StringBuilder; import org.eclipse.jetty.websocket.core.TestMessageHandler;
import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.websocket.core.WebSocketConstants;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.websocket.core.AbstractTestFrameHandler;
import org.eclipse.jetty.websocket.core.CloseStatus;
import org.eclipse.jetty.websocket.core.Frame;
import org.eclipse.jetty.websocket.core.OpCode;
import org.eclipse.jetty.websocket.core.WebSocketTimeoutException;
import static org.eclipse.jetty.websocket.core.OpCode.TEXT; public class AutobahnFrameHandler extends TestMessageHandler
class AutobahnFrameHandler extends AbstractTestFrameHandler
{ {
private static Logger LOG = Log.getLogger(AutobahnFrameHandler.class);
private AtomicBoolean open = new AtomicBoolean(false);
@Override @Override
public void onOpen() public void onOpen(CoreSession coreSession, Callback callback)
{ {
LOG.info("onOpen {}", getCoreSession()); coreSession.setIdleTimeout(Duration.ofSeconds(5));
coreSession.setMaxTextMessageSize(Integer.MAX_VALUE);
if (!open.compareAndSet(false, true)) coreSession.setMaxBinaryMessageSize(Integer.MAX_VALUE);
throw new IllegalStateException(); coreSession.setMaxFrameSize(WebSocketConstants.DEFAULT_MAX_FRAME_SIZE * 2);
} super.onOpen(coreSession, callback);
int count;
@Override
public void onText(Utf8StringBuilder utf8, Callback callback, boolean fin)
{
LOG.debug("onText {} {} {} {}", count++, utf8.length(), fin, getCoreSession());
if (fin)
{
getCoreSession().sendFrame(new Frame(TEXT).setPayload(utf8.toString()),
callback,
false);
}
else
{
callback.succeeded();
}
} }
@Override @Override
public void onBinary(ByteBuffer payload, Callback callback, boolean fin) public void onBinary(ByteBuffer wholeMessage, Callback callback)
{ {
LOG.debug("onBinary {} {} {}", payload == null ? -1 : payload.remaining(), fin, getCoreSession()); sendBinary(wholeMessage, callback, false);
if (fin)
{
Frame echo = new Frame(OpCode.BINARY);
if (payload != null)
echo.setPayload(payload);
getCoreSession().sendFrame(echo, callback, false);
}
else
{
callback.succeeded();
}
} }
@Override @Override
public void onClosed(CloseStatus closeStatus) public void onText(String wholeMessage, Callback callback)
{ {
LOG.info("onClosed {}", closeStatus); sendText(wholeMessage, callback, false);
if (!open.compareAndSet(true, false))
LOG.warn("Already closed or not open {}", closeStatus);
}
@Override
public void onError(Throwable cause)
{
if (cause instanceof WebSocketTimeoutException && open.get())
LOG.info("timeout!");
else
LOG.warn("onError", cause);
} }
} }

View File

@ -16,24 +16,26 @@
// ======================================================================== // ========================================================================
// //
package org.eclipse.jetty.websocket.core.autobahn.client; package org.eclipse.jetty.websocket.core.autobahn;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream;
import java.net.URI; import java.net.URI;
import java.net.URL;
import java.util.Properties;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future; import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException; import java.util.concurrent.TimeoutException;
import org.eclipse.jetty.util.Jetty;
import org.eclipse.jetty.util.UrlEncoded; import org.eclipse.jetty.util.UrlEncoded;
import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger; import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.websocket.core.FrameHandler; import org.eclipse.jetty.websocket.core.FrameHandler;
import org.eclipse.jetty.websocket.core.TestMessageHandler;
import org.eclipse.jetty.websocket.core.client.WebSocketCoreClient; import org.eclipse.jetty.websocket.core.client.WebSocketCoreClient;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
/** /**
* WebSocket Client for use with <a href="https://github.com/crossbario/autobahn-testsuite">autobahn websocket testsuite</a> (wstest). * WebSocket Client for use with <a href="https://github.com/crossbario/autobahn-testsuite">autobahn websocket testsuite</a> (wstest).
* <p> * <p>
@ -70,52 +72,19 @@ import org.eclipse.jetty.websocket.core.client.WebSocketCoreClient;
*/ */
public class AutobahnWebSocketClient public class AutobahnWebSocketClient
{ {
private static final int MBYTE = 1024 * 1024;
private static String getJettyVersion() throws IOException
{
String resource = "META-INF/maven/org.eclipse.jetty.websocket/websocket-core/pom.properties";
URL url = Thread.currentThread().getContextClassLoader().getResource(resource);
if (url == null)
{
if (AutobahnWebSocketClient.class.getPackage() != null)
{
Package pkg = AutobahnWebSocketClient.class.getPackage();
if (pkg.getImplementationVersion() != null)
{
return pkg.getImplementationVersion();
}
}
return "GitMaster";
}
try (InputStream in = url.openStream())
{
Properties props = new Properties();
props.load(in);
return props.getProperty("version");
}
}
public static void main(String[] args) public static void main(String[] args)
{ {
String hostname = "localhost"; String hostname = "localhost";
int port = 9001; int port = 9001;
if (args.length > 0) if (args.length > 0)
{
hostname = args[0]; hostname = args[0];
}
if (args.length > 1) if (args.length > 1)
{
port = Integer.parseInt(args[1]); port = Integer.parseInt(args[1]);
}
int caseNumbers[] = null;
// Optional case numbers // Optional case numbers
// NOTE: these are url query parameter case numbers (whole integers, eg "6"), not the case ids (eg "7.3.1") // NOTE: these are url query parameter case numbers (whole integers, eg "6"), not the case ids (eg "7.3.1")
int[] caseNumbers = null;
if (args.length > 2) if (args.length > 2)
{ {
int offset = 2; int offset = 2;
@ -129,82 +98,71 @@ public class AutobahnWebSocketClient
AutobahnWebSocketClient client = null; AutobahnWebSocketClient client = null;
try try
{ {
String userAgent = "JettyWebsocketClient/" + getJettyVersion(); String userAgent = "JettyWebsocketClient/" + Jetty.VERSION;
client = new AutobahnWebSocketClient(hostname, port, userAgent); client = new AutobahnWebSocketClient(hostname, port, userAgent);
client.updateStatus("Running test suite..."); LOG.info("Running test suite...");
client.updateStatus("Using Fuzzing Server: %s:%d", hostname, port); LOG.info("Using Fuzzing Server: {}:{}", hostname, port);
client.updateStatus("User Agent: %s", userAgent); LOG.info("User Agent: {}", userAgent);
if (caseNumbers == null) if (caseNumbers == null)
{ {
int caseCount = client.getCaseCount(); int caseCount = client.getCaseCount();
client.updateStatus("Will run all %d cases ...", caseCount); LOG.info("Will run all {} cases ...", caseCount);
for (int caseNum = 1; caseNum <= caseCount; caseNum++) for (int caseNum = 1; caseNum <= caseCount; caseNum++)
{ {
client.updateStatus("Running case %d (of %d) ...", caseNum, caseCount); LOG.info("Running case {} (of {}) ...", caseNum, caseCount);
client.runCaseByNumber(caseNum); client.runCaseByNumber(caseNum);
} }
} }
else else
{ {
client.updateStatus("Will run %d cases ...", caseNumbers.length); LOG.info("Will run %d cases ...", caseNumbers.length);
for (int caseNum : caseNumbers) for (int caseNum : caseNumbers)
{ {
client.runCaseByNumber(caseNum); client.runCaseByNumber(caseNum);
} }
} }
client.updateStatus("All test cases executed."); LOG.info("All test cases executed.");
client.updateReports(); client.updateReports();
} }
catch (Throwable t) catch (Throwable t)
{ {
t.printStackTrace(System.err); LOG.warn("Test Failed", t);
} }
finally finally
{ {
if (client != null) if (client != null)
{
client.shutdown(); client.shutdown();
}
} }
System.exit(0);
} }
private Logger log; private static final Logger LOG = Log.getLogger(AutobahnWebSocketClient.class);
private URI baseWebsocketUri; private URI baseWebsocketUri;
private WebSocketCoreClient client; private WebSocketCoreClient client;
private String hostname;
private int port;
private String userAgent; private String userAgent;
public AutobahnWebSocketClient(String hostname, int port, String userAgent) throws Exception public AutobahnWebSocketClient(String hostname, int port, String userAgent) throws Exception
{ {
this.log = Log.getLogger(this.getClass());
this.hostname = hostname;
this.port = port;
this.userAgent = userAgent; this.userAgent = userAgent;
this.baseWebsocketUri = new URI("ws://" + hostname + ":" + port); this.baseWebsocketUri = new URI("ws://" + hostname + ":" + port);
this.client = new WebSocketCoreClient(); this.client = new WebSocketCoreClient();
// TODO: this should be enabled by default
// this.client.getExtensionFactory().register("permessage-deflate",PerMessageDeflateExtension.class);
this.client.start(); this.client.start();
} }
public int getCaseCount() throws IOException, InterruptedException, ExecutionException, TimeoutException public int getCaseCount() throws IOException, InterruptedException
{ {
URI wsUri = baseWebsocketUri.resolve("/getCaseCount"); URI wsUri = baseWebsocketUri.resolve("/getCaseCount");
GetCaseCountHandler onCaseCount = new GetCaseCountHandler(); TestMessageHandler onCaseCount = new TestMessageHandler();
Future<FrameHandler.CoreSession> response = client.connect(onCaseCount, wsUri); Future<FrameHandler.CoreSession> response = client.connect(onCaseCount, wsUri);
if (waitForUpgrade(wsUri, response)) if (waitForUpgrade(wsUri, response))
{ {
onCaseCount.awaitMessage(); String msg = onCaseCount.textMessages.poll(10, TimeUnit.SECONDS);
if (onCaseCount.hasCaseCount()) onCaseCount.getCoreSession().abort(); // Don't expect normal close
{ assertTrue(onCaseCount.closeLatch.await(2, TimeUnit.SECONDS));
return onCaseCount.getCaseCount(); assertNotNull(msg);
} return Integer.decode(msg);
} }
throw new IllegalStateException("Unable to get Case Count"); throw new IllegalStateException("Unable to get Case Count");
} }
@ -212,14 +170,18 @@ public class AutobahnWebSocketClient
public void runCaseByNumber(int caseNumber) throws IOException, InterruptedException public void runCaseByNumber(int caseNumber) throws IOException, InterruptedException
{ {
URI wsUri = baseWebsocketUri.resolve("/runCase?case=" + caseNumber + "&agent=" + UrlEncoded.encodeString(userAgent)); URI wsUri = baseWebsocketUri.resolve("/runCase?case=" + caseNumber + "&agent=" + UrlEncoded.encodeString(userAgent));
log.info("test uri: {}", wsUri); LOG.info("test uri: {}", wsUri);
EchoHandler onEchoMessage = new EchoHandler(caseNumber);
Future<FrameHandler.CoreSession> response = client.connect(onEchoMessage, wsUri);
AutobahnFrameHandler echoHandler = new AutobahnFrameHandler();
Future<FrameHandler.CoreSession> response = client.connect(echoHandler, wsUri);
if (waitForUpgrade(wsUri, response)) if (waitForUpgrade(wsUri, response))
{ {
onEchoMessage.awaitClose(); // Wait up to 5 min as some of the tests can take a while
if (!echoHandler.closeLatch.await(5, TimeUnit.MINUTES))
{
LOG.warn("could not close {}, aborting session", echoHandler);
echoHandler.coreSession.abort();
}
} }
} }
@ -227,26 +189,23 @@ public class AutobahnWebSocketClient
{ {
try try
{ {
this.client.stop(); client.stop();
} }
catch (Exception e) catch (Exception e)
{ {
log.warn("Unable to stop WebSocketClient", e); LOG.warn("Unable to stop WebSocketClient", e);
} }
} }
public void updateReports() throws IOException, InterruptedException, ExecutionException, TimeoutException public void updateReports() throws IOException, InterruptedException, ExecutionException, TimeoutException
{ {
URI wsUri = baseWebsocketUri.resolve("/updateReports?agent=" + UrlEncoded.encodeString(userAgent)); URI wsUri = baseWebsocketUri.resolve("/updateReports?agent=" + UrlEncoded.encodeString(userAgent));
UpdateReportsHandler onUpdateReports = new UpdateReportsHandler(); TestMessageHandler onUpdateReports = new TestMessageHandler();
Future<FrameHandler.CoreSession> response = client.connect(onUpdateReports, wsUri); Future<FrameHandler.CoreSession> response = client.connect(onUpdateReports, wsUri);
response.get(5, TimeUnit.SECONDS); response.get(5, TimeUnit.SECONDS);
onUpdateReports.awaitClose(); assertTrue(onUpdateReports.closeLatch.await(15, TimeUnit.SECONDS));
} LOG.info("Reports updated.");
LOG.info("Test suite finished!");
public void updateStatus(String format, Object... args)
{
log.info(String.format(format, args));
} }
private boolean waitForUpgrade(URI wsUri, Future<FrameHandler.CoreSession> response) throws InterruptedException private boolean waitForUpgrade(URI wsUri, Future<FrameHandler.CoreSession> response) throws InterruptedException
@ -256,14 +215,9 @@ public class AutobahnWebSocketClient
response.get(10, TimeUnit.SECONDS); response.get(10, TimeUnit.SECONDS);
return true; return true;
} }
catch (ExecutionException e) catch (Throwable t)
{ {
log.warn("Unable to connect to: " + wsUri, e); LOG.warn("Unable to connect to: " + wsUri, t);
return false;
}
catch (TimeoutException e)
{
log.warn("Unable to connect to: " + wsUri, e);
return false; return false;
} }
} }

View File

@ -1,110 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995-2019 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.websocket.core.autobahn;
import java.io.IOException;
import java.time.Duration;
import java.util.List;
import org.eclipse.jetty.io.ByteBufferPool;
import org.eclipse.jetty.util.DecoratedObjectFactory;
import org.eclipse.jetty.websocket.core.ExtensionConfig;
import org.eclipse.jetty.websocket.core.FrameHandler;
import org.eclipse.jetty.websocket.core.WebSocketExtensionRegistry;
import org.eclipse.jetty.websocket.core.server.Negotiation;
import org.eclipse.jetty.websocket.core.server.WebSocketNegotiator;
class AutobahnWebSocketNegotiator implements WebSocketNegotiator
{
final DecoratedObjectFactory objectFactory;
final WebSocketExtensionRegistry extensionRegistry;
final ByteBufferPool bufferPool;
public AutobahnWebSocketNegotiator(DecoratedObjectFactory objectFactory, WebSocketExtensionRegistry extensionRegistry, ByteBufferPool bufferPool)
{
this.objectFactory = objectFactory;
this.extensionRegistry = extensionRegistry;
this.bufferPool = bufferPool;
}
@Override
public FrameHandler negotiate(Negotiation negotiation) throws IOException
{
// Finalize negotiations in API layer involves:
// TODO need access to real request/response????
// + MAY read request and set response headers
// + MAY reject with sendError semantics
// + MAY change/add/remove offered extensions
// + MUST pick subprotocol
// + MUST return the FrameHandler or null or exception?
// Examples of those steps are below:
// + MAY read request and set response headers
String special = negotiation.getRequest().getHeader("MySpecialHeader");
if (special != null)
negotiation.getResponse().setHeader("MySpecialHeader", "OK:" + special);
// + MAY reject with sendError semantics
if ("abort".equals(special))
{
negotiation.getResponse().sendError(401, "Some Auth reason");
return null;
}
// + MAY change extensions by mutating response headers
List<ExtensionConfig> offeredExtensions = negotiation.getOfferedExtensions();
// negotiateResponse.addHeader(HttpHeader.SEC_WEBSOCKET_EXTENSIONS.asString(),"@identity");
// + MUST pick subprotocol
List<String> subprotocols = negotiation.getOfferedSubprotocols();
String subprotocol = (subprotocols == null || subprotocols.isEmpty()) ? null : subprotocols.get(0);
negotiation.setSubprotocol(subprotocol);
// + MUST return the FrameHandler or null or exception?
return new AutobahnFrameHandler();
}
@Override
public void customize(FrameHandler.Configuration configurable)
{
configurable.setIdleTimeout(Duration.ofMillis(10000));
configurable.setMaxTextMessageSize(Integer.MAX_VALUE);
configurable.setMaxBinaryMessageSize(Integer.MAX_VALUE);
configurable.setMaxFrameSize(65536 * 2);
}
@Override
public WebSocketExtensionRegistry getExtensionRegistry()
{
return extensionRegistry;
}
@Override
public DecoratedObjectFactory getObjectFactory()
{
return objectFactory;
}
@Override
public ByteBufferPool getByteBufferPool()
{
return bufferPool;
}
}

View File

@ -18,14 +18,9 @@
package org.eclipse.jetty.websocket.core.autobahn; package org.eclipse.jetty.websocket.core.autobahn;
import org.eclipse.jetty.server.HttpConnectionFactory;
import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector; import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.server.handler.ContextHandler; import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.util.DecoratedObjectFactory;
import org.eclipse.jetty.websocket.core.TestUpgradeHandler;
import org.eclipse.jetty.websocket.core.WebSocketExtensionRegistry;
import org.eclipse.jetty.websocket.core.server.WebSocketNegotiator;
import org.eclipse.jetty.websocket.core.server.WebSocketUpgradeHandler; import org.eclipse.jetty.websocket.core.server.WebSocketUpgradeHandler;
/** /**
@ -68,25 +63,16 @@ public class AutobahnWebSocketServer
{ {
int port = 9001; // same port as found in fuzzing-client.json int port = 9001; // same port as found in fuzzing-client.json
if (args != null && args.length > 0) if (args != null && args.length > 0)
{
port = Integer.parseInt(args[0]); port = Integer.parseInt(args[0]);
}
Server server = new Server(port); Server server = new Server(port);
ServerConnector connector = new ServerConnector(server);
ServerConnector connector = new ServerConnector(
server,
new HttpConnectionFactory()
);
connector.setIdleTimeout(10000); connector.setIdleTimeout(10000);
server.addConnector(connector); server.addConnector(connector);
ContextHandler context = new ContextHandler("/"); ContextHandler context = new ContextHandler("/");
server.setHandler(context); server.setHandler(context);
WebSocketNegotiator negotiator =
new AutobahnWebSocketNegotiator(new DecoratedObjectFactory(), new WebSocketExtensionRegistry(), connector.getByteBufferPool());
WebSocketUpgradeHandler handler = new TestUpgradeHandler(negotiator); WebSocketUpgradeHandler handler = new WebSocketUpgradeHandler((neg) -> new AutobahnFrameHandler());
context.setHandler(handler); context.setHandler(handler);
server.start(); server.start();

View File

@ -1,96 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995-2019 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.websocket.core.autobahn.client;
import java.nio.ByteBuffer;
import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.Utf8StringBuilder;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.websocket.core.AbstractTestFrameHandler;
import org.eclipse.jetty.websocket.core.CloseStatus;
public abstract class AbstractClientFrameHandler extends AbstractTestFrameHandler
{
protected final Logger LOG;
public AbstractClientFrameHandler()
{
LOG = Log.getLogger(this.getClass());
}
@Override
public void onOpen()
{
LOG.info("onOpen({})", getCoreSession());
}
@Override
public void onClosed(CloseStatus closeStatus)
{
LOG.debug("onClosed({})", closeStatus);
}
@Override
public void onText(Utf8StringBuilder utf8, Callback callback, boolean fin)
{
LOG.debug("onText len={} fin={}", utf8.length(), fin);
if (fin)
onWholeText(utf8.toString());
callback.succeeded();
}
protected void onWholeText(String message)
{
}
@Override
public void onBinary(ByteBuffer payload, Callback callback, boolean fin)
{
if (fin)
onWholeBinary(payload);
callback.succeeded();
}
protected void onWholeBinary(ByteBuffer payload)
{
}
/**
* Make a copy of a byte buffer.
* <p>
* This is important in some tests, as the underlying byte buffer contained in a Frame can be modified through
* masking and make it difficult to compare the results in the fuzzer.
*
* @param payload the payload to copy
* @return a new byte array of the payload contents
*/
@SuppressWarnings("Duplicates")
public ByteBuffer copyOf(ByteBuffer payload)
{
if (payload == null)
return null;
ByteBuffer copy = ByteBuffer.allocate(payload.remaining());
copy.put(payload.slice());
copy.flip();
return copy;
}
}

View File

@ -1,105 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995-2019 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.websocket.core.autobahn.client;
import java.nio.ByteBuffer;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.Utf8StringBuilder;
import org.eclipse.jetty.websocket.core.CloseStatus;
import org.eclipse.jetty.websocket.core.Frame;
import org.eclipse.jetty.websocket.core.OpCode;
import org.eclipse.jetty.websocket.core.WebSocketTimeoutException;
public class EchoHandler extends AbstractClientFrameHandler
{
private final int currentCaseId;
private CountDownLatch latch = new CountDownLatch(1);
public EchoHandler(int currentCaseId)
{
this.currentCaseId = currentCaseId;
}
public void awaitClose() throws InterruptedException
{
latch.await(5, TimeUnit.SECONDS);
}
@Override
public void onOpen()
{
super.onOpen();
LOG.info("Executing test case {}", currentCaseId);
}
int count;
@Override
public void onText(Utf8StringBuilder utf8, Callback callback, boolean fin)
{
LOG.debug("onText {} {} {} {}", count++, utf8.length(), fin, getCoreSession());
if (fin)
{
Frame echo = new Frame(OpCode.TEXT).setPayload(utf8.toString());
LOG.debug("onText echo {}", echo);
getCoreSession().sendFrame(echo, callback, false);
}
else
{
callback.succeeded();
}
}
@Override
public void onBinary(ByteBuffer payload, Callback callback, boolean fin)
{
LOG.debug("onBinary {} {} {}", payload == null ? -1 : payload.remaining(), fin, getCoreSession());
if (fin)
{
Frame echo = new Frame(OpCode.BINARY);
if (payload != null)
echo.setPayload(payload);
getCoreSession().sendFrame(echo, callback, false);
}
else
{
callback.succeeded();
}
}
@Override
public void onError(Throwable cause)
{
if (cause instanceof WebSocketTimeoutException)
LOG.debug("timeout!");
else
LOG.warn("onError", cause);
}
@Override
public void onClosed(CloseStatus closeStatus)
{
LOG.info("onClosed {}", closeStatus);
super.onClosed(closeStatus);
latch.countDown();
}
}

View File

@ -1,54 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995-2019 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.websocket.core.autobahn.client;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
/**
* Get the autobahn case count
*/
public class GetCaseCountHandler extends AbstractClientFrameHandler
{
private Integer casecount = null;
private CountDownLatch latch = new CountDownLatch(1);
public void awaitMessage() throws InterruptedException
{
latch.await(1, TimeUnit.SECONDS);
}
public int getCaseCount()
{
return casecount.intValue();
}
public boolean hasCaseCount()
{
return (casecount != null);
}
@Override
protected void onWholeText(String message)
{
casecount = Integer.decode(message);
latch.countDown();
LOG.info("Running {} Autobahn testcase", casecount);
}
}

View File

@ -1,51 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995-2019 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.websocket.core.autobahn.client;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import org.eclipse.jetty.websocket.core.CloseStatus;
public class UpdateReportsHandler extends AbstractClientFrameHandler
{
private CountDownLatch latch = new CountDownLatch(1);
public void awaitClose() throws InterruptedException
{
latch.await(15, TimeUnit.SECONDS);
}
@Override
public void onOpen()
{
super.onOpen();
LOG.info("Updating reports ...");
}
@Override
public void onClosed(CloseStatus closeStatus)
{
super.onClosed(closeStatus);
LOG.debug("onClose({})", closeStatus);
LOG.info("Reports updated.");
LOG.info("Test suite finished!");
latch.countDown();
}
}

View File

@ -22,6 +22,7 @@ import java.io.IOException;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import javax.servlet.ServletException; import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
@ -71,7 +72,7 @@ public class ChatWebSocketServer
public void onOpen(CoreSession coreSession, Callback callback) public void onOpen(CoreSession coreSession, Callback callback)
{ {
LOG.debug("onOpen {}", coreSession); LOG.debug("onOpen {}", coreSession);
setMaxTextMessageSize(2 * 1024); coreSession.setMaxTextMessageSize(2 * 1024);
super.onOpen(coreSession, Callback.from(() -> super.onOpen(coreSession, Callback.from(() ->
{ {
members.add(this); members.add(this);
@ -123,7 +124,7 @@ public class ChatWebSocketServer
{ {
@Override @Override
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException throws IOException, ServletException
{ {
response.setStatus(200); response.setStatus(200);
response.setContentType("text/plain"); response.setContentType("text/plain");

View File

@ -39,7 +39,6 @@ import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.TypeUtil; import org.eclipse.jetty.util.TypeUtil;
import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger; import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.websocket.core.AbstractTestFrameHandler;
import org.eclipse.jetty.websocket.core.Behavior; import org.eclipse.jetty.websocket.core.Behavior;
import org.eclipse.jetty.websocket.core.CapturedHexPayloads; import org.eclipse.jetty.websocket.core.CapturedHexPayloads;
import org.eclipse.jetty.websocket.core.ExtensionConfig; import org.eclipse.jetty.websocket.core.ExtensionConfig;
@ -47,6 +46,7 @@ import org.eclipse.jetty.websocket.core.Frame;
import org.eclipse.jetty.websocket.core.IncomingFramesCapture; import org.eclipse.jetty.websocket.core.IncomingFramesCapture;
import org.eclipse.jetty.websocket.core.OpCode; import org.eclipse.jetty.websocket.core.OpCode;
import org.eclipse.jetty.websocket.core.OutgoingNetworkBytesCapture; import org.eclipse.jetty.websocket.core.OutgoingNetworkBytesCapture;
import org.eclipse.jetty.websocket.core.TestMessageHandler;
import org.eclipse.jetty.websocket.core.WebSocketExtensionRegistry; import org.eclipse.jetty.websocket.core.WebSocketExtensionRegistry;
import org.eclipse.jetty.websocket.core.internal.ExtensionStack; import org.eclipse.jetty.websocket.core.internal.ExtensionStack;
import org.eclipse.jetty.websocket.core.internal.Generator; import org.eclipse.jetty.websocket.core.internal.Generator;
@ -421,7 +421,7 @@ public class DeflateFrameExtensionTest extends AbstractExtensionTest
ExtensionStack exStack = new ExtensionStack(new WebSocketExtensionRegistry(), Behavior.SERVER); ExtensionStack exStack = new ExtensionStack(new WebSocketExtensionRegistry(), Behavior.SERVER);
exStack.negotiate(new DecoratedObjectFactory(), bufferPool, new LinkedList<>(), new LinkedList<>()); exStack.negotiate(new DecoratedObjectFactory(), bufferPool, new LinkedList<>(), new LinkedList<>());
WebSocketCoreSession coreSession = new WebSocketCoreSession(new AbstractTestFrameHandler(), Behavior.SERVER, Negotiated.from(exStack)); WebSocketCoreSession coreSession = new WebSocketCoreSession(new TestMessageHandler(), Behavior.SERVER, Negotiated.from(exStack));
coreSession.setMaxFrameSize(maxMessageSize); coreSession.setMaxFrameSize(maxMessageSize);
return coreSession; return coreSession;
} }

View File

@ -28,13 +28,13 @@ import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.Callback; import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.DecoratedObjectFactory; import org.eclipse.jetty.util.DecoratedObjectFactory;
import org.eclipse.jetty.util.TypeUtil; import org.eclipse.jetty.util.TypeUtil;
import org.eclipse.jetty.websocket.core.AbstractTestFrameHandler;
import org.eclipse.jetty.websocket.core.Behavior; import org.eclipse.jetty.websocket.core.Behavior;
import org.eclipse.jetty.websocket.core.Extension; import org.eclipse.jetty.websocket.core.Extension;
import org.eclipse.jetty.websocket.core.ExtensionConfig; import org.eclipse.jetty.websocket.core.ExtensionConfig;
import org.eclipse.jetty.websocket.core.Frame; import org.eclipse.jetty.websocket.core.Frame;
import org.eclipse.jetty.websocket.core.IncomingFramesCapture; import org.eclipse.jetty.websocket.core.IncomingFramesCapture;
import org.eclipse.jetty.websocket.core.OpCode; import org.eclipse.jetty.websocket.core.OpCode;
import org.eclipse.jetty.websocket.core.TestMessageHandler;
import org.eclipse.jetty.websocket.core.WebSocketExtensionRegistry; import org.eclipse.jetty.websocket.core.WebSocketExtensionRegistry;
import org.eclipse.jetty.websocket.core.internal.ExtensionStack; import org.eclipse.jetty.websocket.core.internal.ExtensionStack;
import org.eclipse.jetty.websocket.core.internal.Negotiated; import org.eclipse.jetty.websocket.core.internal.Negotiated;
@ -157,7 +157,7 @@ public class ExtensionTool
ByteBufferPool bufferPool = new MappedByteBufferPool(); ByteBufferPool bufferPool = new MappedByteBufferPool();
ExtensionStack exStack = new ExtensionStack(new WebSocketExtensionRegistry(), Behavior.SERVER); ExtensionStack exStack = new ExtensionStack(new WebSocketExtensionRegistry(), Behavior.SERVER);
exStack.negotiate(new DecoratedObjectFactory(), bufferPool, new LinkedList<>(), new LinkedList<>()); exStack.negotiate(new DecoratedObjectFactory(), bufferPool, new LinkedList<>(), new LinkedList<>());
WebSocketCoreSession coreSession = new WebSocketCoreSession(new AbstractTestFrameHandler(), Behavior.SERVER, Negotiated.from(exStack)); WebSocketCoreSession coreSession = new WebSocketCoreSession(new TestMessageHandler(), Behavior.SERVER, Negotiated.from(exStack));
return coreSession; return coreSession;
} }
} }