Adding WebSocketSettings, integrating into Parser

This commit is contained in:
Joakim Erdfelt 2012-06-19 12:23:46 -07:00
parent 114ff6878b
commit 9178bce643
17 changed files with 163 additions and 107 deletions

View File

@ -0,0 +1,36 @@
package org.eclipse.jetty.websocket.api;
/**
* Settings for WebSocket operations.
*/
public class WebSocketSettings
{
/**
* The maximum size of a text message during parsing/generating
*/
private int maxTextMessageSize = -1;
/**
* The maximum size of a binary message during parsing/generating
*/
private int maxBinaryMessageSize = -1;
public int getMaxBinaryMessageSize()
{
return maxBinaryMessageSize;
}
public int getMaxTextMessageSize()
{
return maxTextMessageSize;
}
public void setMaxBinaryMessageSize(int maxBinaryMessageSize)
{
this.maxBinaryMessageSize = maxBinaryMessageSize;
}
public void setMaxTextMessageSize(int maxTextMessageSize)
{
this.maxTextMessageSize = maxTextMessageSize;
}
}

View File

@ -36,10 +36,6 @@ public class BaseFrame
private boolean masked = false;
private int payloadLength;
private byte mask[];
public final static int FLAG_FIN = 0x8;
public final static int FLAG_RSV1 = 0x4;
public final static int FLAG_RSV2 = 0x2;
public final static int FLAG_RSV3 = 0x1;
/**
* Default constructor
@ -155,6 +151,7 @@ public class BaseFrame
public void setMask(byte[] maskingKey)
{
this.mask = maskingKey;
this.masked = (mask != null);
}
public void setMasked(boolean mask)

View File

@ -3,7 +3,6 @@ package org.eclipse.jetty.websocket.frames;
import java.nio.ByteBuffer;
import org.eclipse.jetty.websocket.api.OpCode;
import org.eclipse.jetty.websocket.api.WebSocketException;
public abstract class ControlFrame extends BaseFrame
{
@ -29,21 +28,14 @@ public abstract class ControlFrame extends BaseFrame
return payload;
}
public void setPayload(ByteBuffer payload)
{
if ( payload.array().length >= 126 )
{
this.payload = payload;
setPayloadLength(payload.array().length);
}
else
{
throw new WebSocketException("too long, catch this better");
}
}
public boolean hasPayload()
{
return payload != null;
}
public void setPayload(ByteBuffer payload)
{
this.payload = payload;
setPayloadLength(payload.array().length);
}
}

View File

@ -37,6 +37,6 @@ public class PingFrame extends ControlFrame
@Override
public String toString()
{
return String.format("%s ping, payload=%s", super.toString(), hasPayload());
return String.format("%s ping, payload[has=%b, string=%s]",super.toString(),hasPayload(),getPayload().toString());
}
}

View File

@ -3,7 +3,6 @@ package org.eclipse.jetty.websocket.generator;
import java.nio.ByteBuffer;
import org.eclipse.jetty.io.ByteBufferPool;
import org.eclipse.jetty.websocket.frames.BaseFrame;
import org.eclipse.jetty.websocket.frames.ControlFrame;
public abstract class ControlFrameGenerator
@ -15,11 +14,6 @@ public abstract class ControlFrameGenerator
this.bufferPool = bufferPool;
}
protected ByteBufferPool getByteBufferPool()
{
return bufferPool;
}
public ByteBuffer generate(ControlFrame frame)
{
// how to calculate the size since control frames may hold
@ -28,7 +22,7 @@ public abstract class ControlFrameGenerator
ByteBuffer buffer = getByteBufferPool().acquire(125,true);
// all control frames are FIN as they can not be fragmented
buffer.putInt(BaseFrame.FLAG_FIN);
buffer.putInt(0x80);
// revisit this on extensions since they can negotiate this
buffer.putInt(0);
@ -37,4 +31,9 @@ public abstract class ControlFrameGenerator
return buffer;
}
protected ByteBufferPool getByteBufferPool()
{
return bufferPool;
}
}

View File

@ -2,6 +2,7 @@ package org.eclipse.jetty.websocket.parser;
import java.nio.ByteBuffer;
import org.eclipse.jetty.websocket.api.WebSocketSettings;
import org.eclipse.jetty.websocket.frames.BinaryFrame;
/**
@ -11,9 +12,9 @@ public class BinaryPayloadParser extends FrameParser<BinaryFrame>
{
private BinaryFrame frame;
public BinaryPayloadParser()
public BinaryPayloadParser(WebSocketSettings settings)
{
super();
super(settings);
frame = new BinaryFrame();
}

View File

@ -2,6 +2,7 @@ package org.eclipse.jetty.websocket.parser;
import java.nio.ByteBuffer;
import org.eclipse.jetty.websocket.api.WebSocketSettings;
import org.eclipse.jetty.websocket.frames.CloseFrame;
/**
@ -11,9 +12,9 @@ public class ClosePayloadParser extends FrameParser<CloseFrame>
{
private CloseFrame frame;
public ClosePayloadParser()
public ClosePayloadParser(WebSocketSettings settings)
{
super();
super(settings);
frame = new CloseFrame();
}

View File

@ -2,6 +2,7 @@ package org.eclipse.jetty.websocket.parser;
import java.nio.ByteBuffer;
import org.eclipse.jetty.websocket.api.WebSocketSettings;
import org.eclipse.jetty.websocket.frames.ContinuationFrame;
/**
@ -11,9 +12,9 @@ public class ContinuationPayloadParser extends FrameParser<ContinuationFrame>
{
private ContinuationFrame frame;
public ContinuationPayloadParser()
public ContinuationPayloadParser(WebSocketSettings settings)
{
super();
super(settings);
frame = new ContinuationFrame();
}

View File

@ -5,6 +5,7 @@ import java.nio.ByteBuffer;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.websocket.api.OpCode;
import org.eclipse.jetty.websocket.api.WebSocketSettings;
import org.eclipse.jetty.websocket.frames.BaseFrame;
/**
@ -43,10 +44,16 @@ public abstract class FrameParser<T extends BaseFrame>
}
private static final Logger LOG = Log.getLogger(FrameParser.class);
private WebSocketSettings settings;
private State state = State.PAYLOAD_LEN;
private int length = 0;
private int cursor = 0;
public FrameParser(WebSocketSettings settings)
{
this.settings = settings;
}
/**
* The frame that is being parsed
*
@ -54,6 +61,11 @@ public abstract class FrameParser<T extends BaseFrame>
*/
public abstract T getFrame();
protected WebSocketSettings getSettings()
{
return settings;
}
/**
* Initialize the base framing values.
*

View File

@ -10,31 +10,11 @@ import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.websocket.api.OpCode;
import org.eclipse.jetty.websocket.api.WebSocketException;
import org.eclipse.jetty.websocket.api.WebSocketSettings;
import org.eclipse.jetty.websocket.frames.BaseFrame;
/**
* Parsing of a frame in WebSocket land.
*
* <pre>
* 0 1 2 3
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
* +-+-+-+-+-------+-+-------------+-------------------------------+
* |F|R|R|R| opcode|M| Payload len | Extended payload length |
* |I|S|S|S| (4) |A| (7) | (16/64) |
* |N|V|V|V| |S| | (if payload len==126/127) |
* | |1|2|3| |K| | |
* +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
* | Extended payload length continued, if payload len == 127 |
* + - - - - - - - - - - - - - - - +-------------------------------+
* | |Masking-key, if MASK set to 1 |
* +-------------------------------+-------------------------------+
* | Masking-key (continued) | Payload Data |
* +-------------------------------- - - - - - - - - - - - - - - - +
* : Payload Data continued ... :
* + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
* | Payload Data continued ... |
* +---------------------------------------------------------------+
* </pre>
* Parsing of a frames in WebSocket land.
*/
public class Parser {
public interface Listener extends EventListener
@ -52,25 +32,32 @@ public class Parser {
private static final Logger LOG = Log.getLogger(Parser.class);
private final List<Listener> listeners = new CopyOnWriteArrayList<>();
private final EnumMap<OpCode, FrameParser<?>> parsers = new EnumMap<>(OpCode.class);
private FrameParser<?> parser;
private WebSocketSettings settings;
private State state = State.FINOP;
private final EnumMap<OpCode, FrameParser> parsers = new EnumMap<>(OpCode.class);
private FrameParser parser;
public Parser()
{
this(new WebSocketSettings());
}
public Parser(WebSocketSettings settings)
{
/*
* TODO: Investigate addition of decompression factory similar to SPDY work in situation of negotiated deflate extension?
*/
this.settings = settings;
reset();
parsers.put(OpCode.CONTINUATION,new ContinuationPayloadParser());
parsers.put(OpCode.TEXT,new TextPayloadParser());
parsers.put(OpCode.BINARY,new BinaryPayloadParser());
parsers.put(OpCode.CLOSE,new ClosePayloadParser());
parsers.put(OpCode.PING,new PingPayloadParser());
parsers.put(OpCode.PONG,new PongPayloadParser());
parsers.put(OpCode.CONTINUATION,new ContinuationPayloadParser(settings));
parsers.put(OpCode.TEXT,new TextPayloadParser(settings));
parsers.put(OpCode.BINARY,new BinaryPayloadParser(settings));
parsers.put(OpCode.CLOSE,new ClosePayloadParser(settings));
parsers.put(OpCode.PING,new PingPayloadParser(settings));
parsers.put(OpCode.PONG,new PongPayloadParser(settings));
}
public void addListener(Listener listener)
@ -78,6 +65,11 @@ public class Parser {
listeners.add(listener);
}
public WebSocketSettings getSettings()
{
return settings;
}
protected void notifyFrame(final BaseFrame f)
{
LOG.debug("Notify Frame: {}",f);
@ -115,16 +107,15 @@ public class Parser {
{
// peek at byte
byte b = buffer.get();
byte flags = (byte)(0xF & (b >> 4));
boolean fin = ((flags & BaseFrame.FLAG_FIN) == 1);
boolean rsv1 = ((flags & BaseFrame.FLAG_RSV1) == 1);
boolean rsv2 = ((flags & BaseFrame.FLAG_RSV2) == 1);
boolean rsv3 = ((flags & BaseFrame.FLAG_RSV3) == 1);
OpCode opcode = OpCode.from((byte)(b & 0xF));
boolean fin = ((b & 0x80) != 0);
boolean rsv1 = ((b & 0x40) != 0);
boolean rsv2 = ((b & 0x20) != 0);
boolean rsv3 = ((b & 0x10) != 0);
OpCode opcode = OpCode.from((byte)(b & 0x0F));
if (opcode.isControlFrame() && !fin)
{
throw new WebSocketException("Fragmented Control Frame");
throw new WebSocketException("Fragmented Control Frame [" + opcode.name() + "]");
}
if (parser == null)
@ -152,6 +143,7 @@ public class Parser {
{
notifyFrame(parser.getFrame());
reset();
state = State.FINOP;
}
break;
}
@ -178,7 +170,10 @@ public class Parser {
public void reset()
{
state = State.FINOP;
parser.reset();
if (parser != null)
{
parser.reset();
}
parser = null;
}
}

View File

@ -2,6 +2,7 @@ package org.eclipse.jetty.websocket.parser;
import java.nio.ByteBuffer;
import org.eclipse.jetty.websocket.api.WebSocketSettings;
import org.eclipse.jetty.websocket.frames.PingFrame;
/**
@ -13,9 +14,9 @@ public class PingPayloadParser extends FrameParser<PingFrame>
private ByteBuffer payload;
private int payloadLength;
public PingPayloadParser()
public PingPayloadParser(WebSocketSettings settings)
{
super();
super(settings);
frame = new PingFrame();
}

View File

@ -2,15 +2,16 @@ package org.eclipse.jetty.websocket.parser;
import java.nio.ByteBuffer;
import org.eclipse.jetty.websocket.api.WebSocketSettings;
import org.eclipse.jetty.websocket.frames.PongFrame;
public class PongPayloadParser extends FrameParser<PongFrame>
{
private PongFrame frame;
public PongPayloadParser()
public PongPayloadParser(WebSocketSettings settings)
{
super();
super(settings);
frame = new PongFrame();
}

View File

@ -2,15 +2,16 @@ package org.eclipse.jetty.websocket.parser;
import java.nio.ByteBuffer;
import org.eclipse.jetty.websocket.api.WebSocketSettings;
import org.eclipse.jetty.websocket.frames.TextFrame;
public class TextPayloadParser extends FrameParser<TextFrame>
{
private TextFrame frame;
public TextPayloadParser()
public TextPayloadParser(WebSocketSettings settings)
{
super();
super(settings);
frame = new TextFrame();
}

View File

@ -2,15 +2,16 @@ package org.eclipse.jetty.websocket.parser;
import java.nio.ByteBuffer;
import org.eclipse.jetty.websocket.api.WebSocketSettings;
import org.eclipse.jetty.websocket.frames.BaseFrame;
public class UnknownPayloadParser extends FrameParser<BaseFrame>
{
private BaseFrame frame;
public UnknownPayloadParser()
public UnknownPayloadParser(WebSocketSettings settings)
{
super();
super(settings);
frame = new BaseFrame();
}

View File

@ -0,0 +1,40 @@
package org.eclipse.jetty.websocket;
import java.nio.ByteBuffer;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.util.log.StdErrLog;
/**
* Utility class for aiding in debugging.
*/
public class Debug
{
public static void dumpState(ByteBuffer buf)
{
System.out.printf("ByteBuffer @%d [%s]%n",buf.hashCode(),buf.toString());
System.out.printf(" - capacity: %d%n",buf.capacity());
System.out.printf(" - hasArray: %b%n",buf.hasArray());
System.out.printf(" - hasRemaining: %b%n",buf.hasRemaining());
System.out.printf(" - isDirect: %b%n",buf.isDirect());
System.out.printf(" - isReadOnly: %b%n",buf.isReadOnly());
System.out.printf(" - limit: %d%n",buf.limit());
System.out.printf(" - order: %s%n",buf.order());
System.out.printf(" - position: %d%n",buf.position());
System.out.printf(" - remaining: %d%n",buf.remaining());
}
public static void enableDebugLogging(Class<?> clazz)
{
setLoggingLevel(clazz,StdErrLog.LEVEL_DEBUG);
}
public static void setLoggingLevel(Class<?> clazz, int level)
{
Logger log = Log.getLogger(clazz);
if(log instanceof StdErrLog) {
((StdErrLog)log).setLevel(level);
}
}
}

View File

@ -1,24 +0,0 @@
package org.eclipse.jetty.websocket;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.util.log.StdErrLog;
/**
* Utility class for managing logging levels during unit testing
*/
public class TestLogging
{
public static void enableDebug(Class<?> clazz)
{
setLevel(clazz,StdErrLog.LEVEL_DEBUG);
}
public static void setLevel(Class<?> clazz, int level)
{
Logger log = Log.getLogger(clazz);
if(log instanceof StdErrLog) {
((StdErrLog)log).setLevel(level);
}
}
}

View File

@ -2,7 +2,7 @@ package org.eclipse.jetty.websocket.parser;
import java.nio.ByteBuffer;
import org.eclipse.jetty.websocket.TestLogging;
import org.eclipse.jetty.websocket.Debug;
import org.eclipse.jetty.websocket.frames.PingFrame;
import org.junit.Test;
@ -11,12 +11,14 @@ public class PingParserTest
@Test
public void testBasicPingParsing()
{
TestLogging.enableDebug(Parser.class);
Debug.enableDebugLogging(Parser.class);
ByteBuffer buf = ByteBuffer.allocate(16);
ByteBuffer buf = ByteBuffer.allocate(7);
buf.put(new byte[]
{ (byte)0x89, 0x05, 0x48, 0x65, 0x6c, 0x6c, 0x6f });
Debug.dumpState(buf);
buf.flip();
Debug.dumpState(buf);
Parser parser = new Parser();
FrameParseCapture capture = new FrameParseCapture();