Adding WebSocketSettings, integrating into Parser
This commit is contained in:
parent
114ff6878b
commit
9178bce643
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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.
|
||||
*
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
|
|
Loading…
Reference in New Issue