Fixing various things around generate/parse of windowed fragments.

+ @WebSocket annotation's policy configuraiton is now optional
+ WebSocketPolicy.clonePolicy() fixed for other fields
+ WebSocketEventDriver now has internal WebSocket POJO specific logger
  to allow for logging control of the WebSocket POJO itself in case of
  runtime exceptions.
+ WebSocketEventDriver now honors bufferSize correctly.
+ DataFrameBytes always requests windowSize from generator, allowing
  generator to determine ultimate byteBuffer utilization itself.
+ MessageInputStream / MessageReader now clears the starting buffer
+ Generator now honors windowSize correctly (even if buffer obtained
  from ByteBufferPool.acquire() is much larger
+ Parser now demasks the payload after a successful parse of the framing
+ Various testing cleanup to produce less noisy output during testing.
This commit is contained in:
Joakim Erdfelt 2012-07-27 09:41:26 -07:00
parent 99bffa6ce0
commit 47f882e6dc
22 changed files with 290 additions and 170 deletions

View File

@ -6,6 +6,9 @@
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES">
<listEntry value="1"/>
</listAttribute>
<listAttribute key="org.eclipse.debug.ui.favoriteGroups">
<listEntry value="org.eclipse.debug.ui.launchGroup.run"/>
</listAttribute>
<stringAttribute key="org.eclipse.jdt.junit.CONTAINER" value=""/>
<booleanAttribute key="org.eclipse.jdt.junit.KEEPRUNNING_ATTR" value="false"/>
<stringAttribute key="org.eclipse.jdt.junit.TESTNAME" value=""/>

View File

@ -30,11 +30,11 @@ import java.lang.annotation.Target;
{ ElementType.TYPE })
public @interface WebSocket
{
int maxBinarySize() default 8192;
int maxBinarySize() default -2;
int maxBufferSize() default 8192;
int maxBufferSize() default -2;
int maxIdleTime() default 300000;
int maxIdleTime() default -2;
int maxTextSize() default 8192;
int maxTextSize() default -2;
}

View File

@ -128,10 +128,12 @@ public class WebSocketPolicy
public WebSocketPolicy clonePolicy()
{
WebSocketPolicy clone = new WebSocketPolicy(this.behavior);
clone.bufferSize = this.bufferSize;
clone.autoFragment = this.autoFragment;
clone.masker = this.masker;
clone.maxBinaryMessageSize = this.maxBinaryMessageSize;
clone.idleTimeout = this.idleTimeout;
clone.bufferSize = this.bufferSize;
clone.maxPayloadSize = this.maxPayloadSize;
clone.maxBinaryMessageSize = this.maxBinaryMessageSize;
clone.maxTextMessageSize = this.maxTextMessageSize;
return clone;
}
@ -146,6 +148,11 @@ public class WebSocketPolicy
return bufferSize;
}
public int getIdleTimeout()
{
return idleTimeout;
}
public Masker getMasker()
{
return masker;
@ -156,11 +163,6 @@ public class WebSocketPolicy
return maxBinaryMessageSize;
}
public int getIdleTimeout()
{
return idleTimeout;
}
public int getMaxPayloadSize()
{
return maxPayloadSize;
@ -186,6 +188,11 @@ public class WebSocketPolicy
this.bufferSize = bufferSize;
}
public void setIdleTimeout(int idleTimeout)
{
this.idleTimeout = idleTimeout;
}
public void setMasker(Masker masker)
{
this.masker = masker;
@ -196,11 +203,6 @@ public class WebSocketPolicy
this.maxBinaryMessageSize = maxBinaryMessageSize;
}
public void setIdleTimeout(int idleTimeout)
{
this.idleTimeout = idleTimeout;
}
public void setMaxPayloadSize(int maxPayloadSize)
{
if (maxPayloadSize < bufferSize)

View File

@ -54,6 +54,7 @@ import org.eclipse.jetty.websocket.protocol.WebSocketFrame;
public class WebSocketEventDriver implements IncomingFrames
{
private static final Logger LOG = Log.getLogger(WebSocketEventDriver.class);
private final Logger socketLog;
private final Object websocket;
private final WebSocketPolicy policy;
private final EventMethods events;
@ -74,29 +75,48 @@ public class WebSocketEventDriver implements IncomingFrames
this.events = methodsCache.getMethods(websocket.getClass());
this.bufferPool = bufferPool;
this.socketLog = Log.getLogger(websocket.getClass());
if (events.isAnnotated())
{
WebSocket anno = websocket.getClass().getAnnotation(WebSocket.class);
// Setup the policy
policy.setBufferSize(anno.maxBufferSize());
policy.setMaxBinaryMessageSize(anno.maxBinarySize());
policy.setMaxTextMessageSize(anno.maxTextSize());
policy.setIdleTimeout(anno.maxIdleTime());
if (anno.maxBufferSize() > 0)
{
this.policy.setBufferSize(anno.maxBufferSize());
}
if (anno.maxBinarySize() > 0)
{
this.policy.setMaxBinaryMessageSize(anno.maxBinarySize());
}
if (anno.maxTextSize() > 0)
{
this.policy.setMaxTextMessageSize(anno.maxTextSize());
}
if (anno.maxIdleTime() > 0)
{
this.policy.setIdleTimeout(anno.maxIdleTime());
}
}
}
private void appendBuffer(ByteBuffer msgBuf, ByteBuffer byteBuffer)
private void appendBuffer(ByteBuffer msgBuf, ByteBuffer payloadBuf)
{
if (byteBuffer == null)
if (payloadBuf == null)
{
// nothing to do (empty payload is possible)
return;
}
if (msgBuf.remaining() < byteBuffer.remaining())
if (msgBuf.remaining() < payloadBuf.remaining())
{
throw new MessageTooLargeException("Message exceeded maximum buffer");
if (LOG.isDebugEnabled())
{
LOG.debug(" msgBuf = {}",BufferUtil.toDetailString(msgBuf));
LOG.debug("payloadBuf = {}",BufferUtil.toDetailString(msgBuf));
}
msgBuf.put(byteBuffer);
throw new MessageTooLargeException("Message exceeded maximum buffer size of [" + payloadBuf.capacity() + "]");
}
msgBuf.put(payloadBuf);
}
public WebSocketPolicy getPolicy()
@ -389,7 +409,7 @@ public class WebSocketEventDriver implements IncomingFrames
private void unhandled(Throwable t)
{
LOG.warn("Unhandled Error (closing connection)",t);
socketLog.warn("Unhandled Error (closing connection)",t);
// Unhandled Error, close the connection.
switch (policy.getBehavior())

View File

@ -62,15 +62,9 @@ public class DataFrameBytes<C> extends FrameBytes<C>
try
{
int windowSize = connection.getPolicy().getBufferSize();
// TODO: create a window size?
size = frame.getPayloadLength();
if (size > windowSize)
{
size = windowSize;
}
buffer = connection.getGenerator().generate(size,frame);
// TODO: windowSize should adjust according to some sort of flow control rules.
buffer = connection.getGenerator().generate(windowSize,frame);
return buffer;
}
catch (Throwable x)

View File

@ -19,6 +19,8 @@ import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import org.eclipse.jetty.util.BufferUtil;
/**
* Support class for reading binary message data as an InputStream.
*/
@ -28,6 +30,7 @@ public class MessageInputStream extends InputStream implements StreamAppender
public MessageInputStream(ByteBuffer buf)
{
BufferUtil.clearToFill(buf);
this.buffer = buf;
}

View File

@ -19,6 +19,8 @@ import java.io.IOException;
import java.io.Reader;
import java.nio.ByteBuffer;
import org.eclipse.jetty.util.BufferUtil;
/**
* Support class for reading text message data as an Reader.
* <p>
@ -30,6 +32,7 @@ public class MessageReader extends Reader implements StreamAppender
public MessageReader(ByteBuffer buf)
{
BufferUtil.clearToFill(buf);
this.buffer = buf;
}

View File

@ -160,15 +160,17 @@ public class Generator
}
/*
* The generate method needs to perform two functions.
*
* 1 - on the initial call for a given frame it needs to generate the framing bytecode and as much of the payload as will fit in the given buffer size
*
* 2 - on subsequent calls it needs to return as much of the payload as will fit in the given buffer size
/**
* Generate, into a ByteBuffer, no more than bufferSize of contents from the frame. If the frame exceeds the bufferSize, then multiple calls to
* {@link #generate(int, WebSocketFrame)} are required to obtain each window of ByteBuffer to complete the frame.
*/
public ByteBuffer generate(int bufferSize, WebSocketFrame frame)
public ByteBuffer generate(int windowSize, WebSocketFrame frame)
{
if (windowSize < OVERHEAD)
{
throw new IllegalArgumentException("Cannot have windowSize less than " + OVERHEAD);
}
if (LOG.isDebugEnabled())
{
StringBuilder dbg = new StringBuilder();
@ -192,8 +194,11 @@ public class Generator
/*
* prepare the byte buffer to put frame into
*/
ByteBuffer buffer = bufferPool.acquire(bufferSize + OVERHEAD,true);
ByteBuffer buffer = bufferPool.acquire(windowSize,true);
BufferUtil.clearToFill(buffer);
// since the buffer from the pool can exceed the window size, artificially
// limit the buffer to the window size.
buffer.limit(buffer.position() + windowSize);
if (frame.remaining() == frame.getPayloadLength())
{

View File

@ -95,41 +95,6 @@ public class Parser
}
}
/**
* Copy the bytes from one buffer to the other, demasking the content if necessary.
*
* @param src
* the source {@link ByteBuffer}
* @param dest
* the destination {@link ByteBuffer}
* @param length
* the length of bytes to worry about
* @return the number of bytes copied
*/
protected int copyBuffer(ByteBuffer src, ByteBuffer dest, int length)
{
int amt = Math.min(length,src.remaining());
if (frame.isMasked())
{
// Demask the content 1 byte at a time
// FIXME: on partially parsed frames this needs an offset from prior parse
byte mask[] = frame.getMask();
for (int i = 0; i < amt; i++)
{
dest.put((byte)(src.get() ^ mask[i % 4]));
}
}
else
{
// Copy the content as-is
// TODO: Look into having a BufferUtil.put(from,to,len) method
byte b[] = new byte[amt];
src.get(b,0,amt);
dest.put(b,0,amt);
}
return amt;
}
public IncomingFrames getIncomingFramesHandler()
{
return incomingFramesHandler;
@ -441,11 +406,23 @@ public class Parser
payload = ByteBuffer.allocate(payloadLength);
}
copyBuffer(buffer,payload,payload.remaining());
BufferUtil.put(buffer,payload);
if (payload.position() >= payloadLength)
{
BufferUtil.flipToFlush(payload,0);
// demask (if needed)
if (frame.isMasked())
{
byte mask[] = frame.getMask();
int end = payload.limit();
for (int i = payload.position(); i < end; i++)
{
payload.put(i,(byte)(payload.get(i) ^ mask[i % 4]));
}
}
frame.setPayload(payload);
this.payload = null;
return true;

View File

@ -391,10 +391,6 @@ public class DeflateFrameExtensionTest
ByteBuffer compressed = actual.getPayload().slice();
ByteBuffer uncompressed = ext.inflate(compressed);
System.err.printf("Expected : %s%n",BufferUtil.toDetailString(expected));
System.err.printf("Compressed : %s%n",BufferUtil.toDetailString(compressed));
System.err.printf("Uncompressed: %s%n",BufferUtil.toDetailString(uncompressed));
Assert.assertThat(prefix + ".payloadLength",uncompressed.remaining(),is(expected.remaining()));
ByteBufferAssert.assertEquals(prefix + ".payload",expected,uncompressed);
}

View File

@ -2,15 +2,19 @@ package org.eclipse.jetty.websocket.io;
import org.eclipse.jetty.websocket.api.WebSocketPolicy;
import org.eclipse.jetty.websocket.driver.WebSocketEventDriver;
import org.eclipse.jetty.websocket.protocol.OutgoingFramesCapture;
import org.junit.rules.TestName;
public class LocalWebSocketSession extends WebSocketSession
{
private String id;
private OutgoingFramesCapture outgoingCapture;
public LocalWebSocketSession(TestName testname)
{
this(testname,null);
outgoingCapture = new OutgoingFramesCapture();
setOutgoing(outgoingCapture);
}
public LocalWebSocketSession(TestName testname, WebSocketEventDriver driver)
@ -19,6 +23,11 @@ public class LocalWebSocketSession extends WebSocketSession
this.id = testname.getMethodName();
}
public OutgoingFramesCapture getOutgoingCapture()
{
return outgoingCapture;
}
@Override
public String toString()
{

View File

@ -12,29 +12,37 @@ import org.junit.Test;
public class GeneratorTest
{
/**
* Test the windowed generate of a frame that has no masking.
*/
@Test
public void testWindowedGenerate()
{
// A decent sized frame, no masking
byte payload[] = new byte[10240];
Arrays.fill(payload,(byte)0x44);
WebSocketFrame frame = WebSocketFrame.binary(payload);
// tracking values
int totalParts = 0;
int totalBytes = 0;
int windowSize = 1024;
int expectedHeaderSize = 4;
int expectedParts = (int)Math.ceil((double)(payload.length + expectedHeaderSize) / windowSize);
// the generator
Generator generator = new UnitGenerator();
// lets see how many parts the generator makes
boolean done = false;
while (!done)
{
Assert.assertThat("Too many parts",totalParts,lessThan(20));
// sanity check in loop, our test should fail if this is true.
Assert.assertThat("Too many parts",totalParts,lessThanOrEqualTo(expectedParts));
ByteBuffer buf = generator.generate(windowSize,frame);
// System.out.printf("Generated buf.limit() = %,d%n",buf.limit());
Assert.assertThat("Generated should not exceed window size",buf.remaining(),lessThanOrEqualTo(windowSize));
totalBytes += buf.remaining();
totalParts++;
@ -42,6 +50,7 @@ public class GeneratorTest
done = (frame.remaining() <= 0);
}
// validate
Assert.assertThat("Created Parts",totalParts,is(expectedParts));
Assert.assertThat("Created Bytes",totalBytes,is(payload.length + expectedHeaderSize));
}
@ -49,6 +58,7 @@ public class GeneratorTest
@Test
public void testWindowedGenerateWithMasking()
{
// A decent sized frame, with masking
byte payload[] = new byte[10240];
Arrays.fill(payload,(byte)0x55);
@ -56,15 +66,17 @@ public class GeneratorTest
{ 0x2A, (byte)0xF0, 0x0F, 0x00 };
WebSocketFrame frame = WebSocketFrame.binary(payload);
frame.setMask(mask);
frame.setMask(mask); // masking!
// tracking values
int totalParts = 0;
int totalBytes = 0;
int windowSize = 2929; // important, use an odd # window size to test masking across window barriers
int windowSize = 2929; // important for test, use an odd # window size to test masking across window barriers
int expectedHeaderSize = 8;
int expectedParts = (int)Math.ceil((double)(payload.length + expectedHeaderSize) / windowSize);
// Buffer to capture generated bytes
// Buffer to capture generated bytes (we do this to validate that the masking
// is working correctly
ByteBuffer completeBuf = ByteBuffer.allocate(payload.length + expectedHeaderSize);
BufferUtil.clearToFill(completeBuf);
@ -74,10 +86,11 @@ public class GeneratorTest
boolean done = false;
while (!done)
{
Assert.assertThat("Too many parts",totalParts,lessThan(20));
// sanity check in loop, our test should fail if this is true.
Assert.assertThat("Too many parts",totalParts,lessThanOrEqualTo(expectedParts));
ByteBuffer buf = generator.generate(windowSize,frame);
// System.out.printf("Generated buf.limit() = %,d%n",buf.limit());
Assert.assertThat("Generated should not exceed window size",buf.remaining(),lessThanOrEqualTo(windowSize));
totalBytes += buf.remaining();
totalParts++;
@ -104,7 +117,7 @@ public class GeneratorTest
Assert.assertThat("Frame.opcode",actual.getOpCode(),is(OpCode.BINARY));
Assert.assertThat("Frame.payloadLength",actual.getPayloadLength(),is(payload.length));
// Validate payload content for proper masking
// Validate payload contents for proper masking
ByteBuffer actualData = actual.getPayload().slice();
Assert.assertThat("Frame.payload.remaining",actualData.remaining(),is(payload.length));
while (actualData.remaining() > 0)

View File

@ -19,11 +19,9 @@ import static org.hamcrest.Matchers.*;
import java.nio.ByteBuffer;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.websocket.api.WebSocketBehavior;
import org.eclipse.jetty.websocket.api.WebSocketPolicy;
import org.eclipse.jetty.websocket.protocol.OpCode;
import org.eclipse.jetty.websocket.protocol.Parser;
import org.eclipse.jetty.websocket.protocol.WebSocketFrame;
import org.junit.Assert;
import org.junit.Test;
@ -41,23 +39,24 @@ public class RFC6455ExamplesParserTest
parser.setIncomingFramesHandler(capture);
ByteBuffer buf = ByteBuffer.allocate(16);
BufferUtil.clearToFill(buf);
// Raw bytes as found in RFC 6455, Section 5.7 - Examples
// A fragmented unmasked text message (part 1 of 2 "Hel")
buf.put(new byte[]
{ (byte)0x01, (byte)0x03, 0x48, (byte)0x65, 0x6c });
buf.flip();
// Parse #1
BufferUtil.flipToFlush(buf,0);
parser.parse(buf);
// part 2 of 2 "lo" (A continuation frame of the prior text message)
buf.flip();
BufferUtil.flipToFill(buf);
buf.put(new byte[]
{ (byte)0x80, 0x02, 0x6c, 0x6f });
buf.flip();
// Parse #2
BufferUtil.flipToFlush(buf,0);
parser.parse(buf);
capture.assertNoErrors();

View File

@ -1,2 +1,2 @@
org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog
org.eclipse.jetty.websocket.LEVEL=DEBUG
org.eclipse.jetty.websocket.LEVEL=WARN

View File

@ -6,6 +6,9 @@
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES">
<listEntry value="1"/>
</listAttribute>
<listAttribute key="org.eclipse.debug.ui.favoriteGroups">
<listEntry value="org.eclipse.debug.ui.launchGroup.run"/>
</listAttribute>
<stringAttribute key="org.eclipse.jdt.junit.CONTAINER" value=""/>
<booleanAttribute key="org.eclipse.jdt.junit.KEEPRUNNING_ATTR" value="false"/>
<stringAttribute key="org.eclipse.jdt.junit.TESTNAME" value=""/>

View File

@ -17,28 +17,21 @@ package org.eclipse.jetty.websocket.server;
import static org.hamcrest.Matchers.*;
import java.io.IOException;
import java.net.SocketException;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.Queue;
import java.util.concurrent.TimeUnit;
import org.eclipse.jetty.util.FutureCallback;
import org.eclipse.jetty.util.Utf8Appendable.NotUtf8Exception;
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.annotations.OnWebSocketConnect;
import org.eclipse.jetty.websocket.annotations.OnWebSocketMessage;
import org.eclipse.jetty.websocket.annotations.WebSocket;
import org.eclipse.jetty.websocket.api.StatusCode;
import org.eclipse.jetty.websocket.api.WebSocketConnection;
import org.eclipse.jetty.websocket.protocol.CloseInfo;
import org.eclipse.jetty.websocket.protocol.Generator;
import org.eclipse.jetty.websocket.protocol.OpCode;
import org.eclipse.jetty.websocket.protocol.WebSocketFrame;
import org.eclipse.jetty.websocket.server.blockhead.BlockheadClient;
import org.eclipse.jetty.websocket.server.helper.RFCServlet;
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.BeforeClass;
@ -52,68 +45,6 @@ import org.junit.Test;
*/
public class WebSocketServletRFCTest
{
@SuppressWarnings("serial")
public static class RFCServlet extends WebSocketServlet
{
@Override
public void registerWebSockets(WebSocketServerFactory factory)
{
factory.register(RFCSocket.class);
}
}
@WebSocket
public static class RFCSocket
{
private static Logger LOG = Log.getLogger(RFCSocket.class);
private WebSocketConnection conn;
@OnWebSocketMessage
public void onBinary(byte buf[], int offset, int len)
{
LOG.debug("onBinary(byte[{}],{},{})",buf.length,offset,len);
// echo the message back.
try
{
this.conn.write(null,new FutureCallback<Void>(),buf,offset,len);
}
catch (IOException e)
{
e.printStackTrace(System.err);
}
}
@OnWebSocketConnect
public void onOpen(WebSocketConnection conn)
{
this.conn = conn;
}
@OnWebSocketMessage
public void onText(String message)
{
LOG.debug("onText({})",message);
// Test the RFC 6455 close code 1011 that should close
// trigger a WebSocket server terminated close.
if (message.equals("CRASH"))
{
throw new RuntimeException("Something bad happened");
}
// echo the message back.
try
{
this.conn.write(null,new FutureCallback<Void>(),message);
}
catch (IOException e)
{
e.printStackTrace(System.err);
}
}
}
private static Generator generator = new UnitGenerator();
private static SimpleServletServer server;

View File

@ -0,0 +1,24 @@
package org.eclipse.jetty.websocket.server.ab;
import org.eclipse.jetty.websocket.server.WebSocketServerFactory;
import org.eclipse.jetty.websocket.server.WebSocketServlet;
/**
* Servlet with bigger message policy sizes, with registered simple echo socket.
*/
@SuppressWarnings("serial")
public class ABServlet extends WebSocketServlet
{
private static final int KBYTE = 1024;
private static final int MBYTE = KBYTE * KBYTE;
@Override
public void registerWebSockets(WebSocketServerFactory factory)
{
factory.register(ABSocket.class);
factory.getPolicy().setBufferSize(2 * MBYTE);
factory.getPolicy().setMaxTextMessageSize(2 * MBYTE);
factory.getPolicy().setMaxBinaryMessageSize(2 * MBYTE);
}
}

View File

@ -0,0 +1,60 @@
package org.eclipse.jetty.websocket.server.ab;
import java.io.IOException;
import org.eclipse.jetty.util.FutureCallback;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.websocket.annotations.OnWebSocketConnect;
import org.eclipse.jetty.websocket.annotations.OnWebSocketMessage;
import org.eclipse.jetty.websocket.annotations.WebSocket;
import org.eclipse.jetty.websocket.api.WebSocketConnection;
/**
* Simple Echo WebSocket, using async writes of echo
*/
@WebSocket
public class ABSocket
{
private static Logger LOG = Log.getLogger(ABSocket.class);
private WebSocketConnection conn;
@OnWebSocketMessage
public void onBinary(byte buf[], int offset, int len)
{
LOG.debug("onBinary(byte[{}],{},{})",buf.length,offset,len);
// echo the message back.
try
{
this.conn.write(null,new FutureCallback<Void>(),buf,offset,len);
}
catch (IOException e)
{
e.printStackTrace(System.err);
}
}
@OnWebSocketConnect
public void onOpen(WebSocketConnection conn)
{
this.conn = conn;
}
@OnWebSocketMessage
public void onText(String message)
{
LOG.debug("onText({})",message);
// echo the message back.
try
{
this.conn.write(null,new FutureCallback<Void>(),message);
}
catch (IOException e)
{
e.printStackTrace(System.err);
}
}
}

View File

@ -3,7 +3,6 @@ package org.eclipse.jetty.websocket.server.ab;
import java.nio.ByteBuffer;
import org.eclipse.jetty.websocket.server.SimpleServletServer;
import org.eclipse.jetty.websocket.server.examples.MyEchoServlet;
import org.junit.AfterClass;
import org.junit.BeforeClass;
@ -20,7 +19,7 @@ public abstract class AbstractABCase
@BeforeClass
public static void startServer() throws Exception
{
server = new SimpleServletServer(new MyEchoServlet());
server = new SimpleServletServer(new ABServlet());
server.start();
}

View File

@ -0,0 +1,14 @@
package org.eclipse.jetty.websocket.server.helper;
import org.eclipse.jetty.websocket.server.WebSocketServerFactory;
import org.eclipse.jetty.websocket.server.WebSocketServlet;
@SuppressWarnings("serial")
public class RFCServlet extends WebSocketServlet
{
@Override
public void registerWebSockets(WebSocketServerFactory factory)
{
factory.register(RFCSocket.class);
}
}

View File

@ -0,0 +1,63 @@
package org.eclipse.jetty.websocket.server.helper;
import java.io.IOException;
import org.eclipse.jetty.util.FutureCallback;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.websocket.annotations.OnWebSocketConnect;
import org.eclipse.jetty.websocket.annotations.OnWebSocketMessage;
import org.eclipse.jetty.websocket.annotations.WebSocket;
import org.eclipse.jetty.websocket.api.WebSocketConnection;
@WebSocket
public class RFCSocket
{
private static Logger LOG = Log.getLogger(RFCSocket.class);
private WebSocketConnection conn;
@OnWebSocketMessage
public void onBinary(byte buf[], int offset, int len)
{
LOG.debug("onBinary(byte[{}],{},{})",buf.length,offset,len);
// echo the message back.
try
{
this.conn.write(null,new FutureCallback<Void>(),buf,offset,len);
}
catch (IOException e)
{
e.printStackTrace(System.err);
}
}
@OnWebSocketConnect
public void onOpen(WebSocketConnection conn)
{
this.conn = conn;
}
@OnWebSocketMessage
public void onText(String message)
{
LOG.debug("onText({})",message);
// Test the RFC 6455 close code 1011 that should close
// trigger a WebSocket server terminated close.
if (message.equals("CRASH"))
{
throw new RuntimeException("Something bad happened");
}
// echo the message back.
try
{
this.conn.write(null,new FutureCallback<Void>(),message);
}
catch (IOException e)
{
e.printStackTrace(System.err);
}
}
}

View File

@ -1,8 +1,10 @@
org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog
org.eclipse.jetty.io.LEVEL=WARN
org.eclipse.jetty.server.LEVEL=WARN
org.eclipse.jetty.websocket.server.helper.RFCSocket.LEVEL=OFF
# org.eclipse.jetty.util.thread.QueuedThreadPool.LEVEL=DEBUG
# org.eclipse.jetty.io.SelectorManager.LEVEL=INFO
org.eclipse.jetty.websocket.LEVEL=DEBUG
# org.eclipse.jetty.websocket.LEVEL=DEBUG
# org.eclipse.jetty.websocket.extensions.LEVEL=DEBUG
# org.eclipse.jetty.websocket.protocol.Generator.LEVEL=INFO
# org.eclipse.jetty.websocket.protocol.Parser.LEVEL=INFO