Merge branch 'jetty-9' of ssh://git.eclipse.org/gitroot/jetty/org.eclipse.jetty.project into jetty-9
This commit is contained in:
commit
285701f98e
|
@ -6,8 +6,11 @@ import org.eclipse.jetty.util.BufferUtil;
|
||||||
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.api.CloseException;
|
import org.eclipse.jetty.websocket.api.CloseException;
|
||||||
|
import org.eclipse.jetty.websocket.api.ProtocolException;
|
||||||
import org.eclipse.jetty.websocket.api.StatusCode;
|
import org.eclipse.jetty.websocket.api.StatusCode;
|
||||||
|
import org.eclipse.jetty.websocket.api.WebSocketException;
|
||||||
import org.eclipse.jetty.websocket.api.WebSocketPolicy;
|
import org.eclipse.jetty.websocket.api.WebSocketPolicy;
|
||||||
|
import org.eclipse.jetty.websocket.protocol.CloseInfo;
|
||||||
import org.eclipse.jetty.websocket.protocol.OpCode;
|
import org.eclipse.jetty.websocket.protocol.OpCode;
|
||||||
import org.eclipse.jetty.websocket.protocol.WebSocketFrame;
|
import org.eclipse.jetty.websocket.protocol.WebSocketFrame;
|
||||||
|
|
||||||
|
@ -39,6 +42,8 @@ public class FrameParser
|
||||||
{
|
{
|
||||||
private enum State
|
private enum State
|
||||||
{
|
{
|
||||||
|
START,
|
||||||
|
FINOP,
|
||||||
PAYLOAD_LEN,
|
PAYLOAD_LEN,
|
||||||
PAYLOAD_LEN_BYTES,
|
PAYLOAD_LEN_BYTES,
|
||||||
MASK,
|
MASK,
|
||||||
|
@ -49,8 +54,7 @@ public class FrameParser
|
||||||
private static final Logger LOG = Log.getLogger(FrameParser.class);
|
private static final Logger LOG = Log.getLogger(FrameParser.class);
|
||||||
private WebSocketPolicy policy;
|
private WebSocketPolicy policy;
|
||||||
// State specific
|
// State specific
|
||||||
private State state = State.PAYLOAD_LEN;
|
private State state = State.START;
|
||||||
private long length = 0;
|
|
||||||
private int cursor = 0;
|
private int cursor = 0;
|
||||||
// Frame
|
// Frame
|
||||||
private WebSocketFrame frame;
|
private WebSocketFrame frame;
|
||||||
|
@ -123,25 +127,6 @@ public class FrameParser
|
||||||
return policy;
|
return policy;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Initialize the base framing values.
|
|
||||||
*
|
|
||||||
* @param fin
|
|
||||||
* @param rsv1
|
|
||||||
* @param rsv2
|
|
||||||
* @param rsv3
|
|
||||||
* @param opcode
|
|
||||||
*/
|
|
||||||
public final void initFrame(boolean fin, boolean rsv1, boolean rsv2, boolean rsv3, OpCode opcode)
|
|
||||||
{
|
|
||||||
WebSocketFrame frame = new WebSocketFrame();
|
|
||||||
frame.setFin(fin);
|
|
||||||
frame.setRsv1(rsv1);
|
|
||||||
frame.setRsv2(rsv2);
|
|
||||||
frame.setRsv3(rsv3);
|
|
||||||
frame.setOpCode(opcode);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parse the base framing protocol buffer.
|
* Parse the base framing protocol buffer.
|
||||||
* <p>
|
* <p>
|
||||||
|
@ -153,37 +138,91 @@ public class FrameParser
|
||||||
* the buffer to parse from.
|
* the buffer to parse from.
|
||||||
* @return true if done parsing base framing protocol and ready for parsing of the payload. false if incomplete parsing of base framing protocol.
|
* @return true if done parsing base framing protocol and ready for parsing of the payload. false if incomplete parsing of base framing protocol.
|
||||||
*/
|
*/
|
||||||
public final boolean parseBaseFraming(ByteBuffer buffer)
|
public boolean parse(ByteBuffer buffer)
|
||||||
{
|
{
|
||||||
LOG.debug("Parsing {} bytes",buffer.remaining());
|
LOG.debug("Parsing {} bytes",buffer.remaining());
|
||||||
while (buffer.hasRemaining())
|
while (buffer.hasRemaining())
|
||||||
{
|
{
|
||||||
switch (state)
|
switch (state)
|
||||||
{
|
{
|
||||||
|
case START:
|
||||||
|
{
|
||||||
|
if ((frame != null) && (frame.isFin()))
|
||||||
|
{
|
||||||
|
frame.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
state = State.FINOP;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case FINOP:
|
||||||
|
{
|
||||||
|
// peek at byte
|
||||||
|
byte b = buffer.get();
|
||||||
|
boolean fin = ((b & 0x80) != 0);
|
||||||
|
boolean rsv1 = ((b & 0x40) != 0);
|
||||||
|
boolean rsv2 = ((b & 0x20) != 0);
|
||||||
|
boolean rsv3 = ((b & 0x10) != 0);
|
||||||
|
byte opc = (byte)(b & 0x0F);
|
||||||
|
OpCode opcode = OpCode.from(opc);
|
||||||
|
|
||||||
|
if (opcode == null)
|
||||||
|
{
|
||||||
|
throw new WebSocketException("Unknown opcode: " + opc);
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG.debug("OpCode {}, fin={}",opcode.name(),fin);
|
||||||
|
|
||||||
|
if (opcode.isControlFrame() && !fin)
|
||||||
|
{
|
||||||
|
throw new ProtocolException("Fragmented Control Frame [" + opcode.name() + "]");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (opcode == OpCode.CONTINUATION)
|
||||||
|
{
|
||||||
|
if (frame == null)
|
||||||
|
{
|
||||||
|
throw new ProtocolException("Fragment continuation frame without prior !FIN");
|
||||||
|
}
|
||||||
|
// Be careful to use the original opcode
|
||||||
|
opcode = frame.getOpCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
// base framing flags
|
||||||
|
frame = new WebSocketFrame();
|
||||||
|
frame.setFin(fin);
|
||||||
|
frame.setRsv1(rsv1);
|
||||||
|
frame.setRsv2(rsv2);
|
||||||
|
frame.setRsv3(rsv3);
|
||||||
|
frame.setOpCode(opcode);
|
||||||
|
|
||||||
|
state = State.PAYLOAD_LEN;
|
||||||
|
break;
|
||||||
|
}
|
||||||
case PAYLOAD_LEN:
|
case PAYLOAD_LEN:
|
||||||
{
|
{
|
||||||
byte b = buffer.get();
|
byte b = buffer.get();
|
||||||
getFrame().setMasked((b & 0x80) != 0);
|
getFrame().setMasked((b & 0x80) != 0);
|
||||||
length = (byte)(0x7F & b);
|
payloadLength = (byte)(0x7F & b);
|
||||||
|
|
||||||
if (length == 127)
|
if (payloadLength == 127)
|
||||||
{
|
{
|
||||||
// length 8 bytes (extended payload length)
|
// length 8 bytes (extended payload length)
|
||||||
length = 0;
|
payloadLength = 0;
|
||||||
state = State.PAYLOAD_LEN_BYTES;
|
state = State.PAYLOAD_LEN_BYTES;
|
||||||
cursor = 8;
|
cursor = 8;
|
||||||
break; // continue onto next state
|
break; // continue onto next state
|
||||||
}
|
}
|
||||||
else if (length == 126)
|
else if (payloadLength == 126)
|
||||||
{
|
{
|
||||||
// length 2 bytes (extended payload length)
|
// length 2 bytes (extended payload length)
|
||||||
length = 0;
|
payloadLength = 0;
|
||||||
state = State.PAYLOAD_LEN_BYTES;
|
state = State.PAYLOAD_LEN_BYTES;
|
||||||
cursor = 2;
|
cursor = 2;
|
||||||
break; // continue onto next state
|
break; // continue onto next state
|
||||||
}
|
}
|
||||||
|
|
||||||
assertSanePayloadLength(length);
|
assertSanePayloadLength(payloadLength);
|
||||||
if (getFrame().isMasked())
|
if (getFrame().isMasked())
|
||||||
{
|
{
|
||||||
state = State.MASK;
|
state = State.MASK;
|
||||||
|
@ -199,10 +238,10 @@ public class FrameParser
|
||||||
{
|
{
|
||||||
byte b = buffer.get();
|
byte b = buffer.get();
|
||||||
--cursor;
|
--cursor;
|
||||||
length |= (b & 0xFF) << (8 * cursor);
|
payloadLength |= (b & 0xFF) << (8 * cursor);
|
||||||
if (cursor == 0)
|
if (cursor == 0)
|
||||||
{
|
{
|
||||||
assertSanePayloadLength(length);
|
assertSanePayloadLength(payloadLength);
|
||||||
if (getFrame().isMasked())
|
if (getFrame().isMasked())
|
||||||
{
|
{
|
||||||
state = State.MASK;
|
state = State.MASK;
|
||||||
|
@ -241,11 +280,21 @@ public class FrameParser
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
case PAYLOAD:
|
||||||
|
{
|
||||||
if (state == State.PAYLOAD)
|
if (parsePayload(buffer))
|
||||||
{
|
{
|
||||||
return true;
|
// special check for close
|
||||||
|
if (frame.getOpCode() == OpCode.CLOSE)
|
||||||
|
{
|
||||||
|
new CloseInfo(frame);
|
||||||
|
}
|
||||||
|
state = State.START;
|
||||||
|
// we have a frame!
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -261,7 +310,6 @@ public class FrameParser
|
||||||
*/
|
*/
|
||||||
public boolean parsePayload(ByteBuffer buffer)
|
public boolean parsePayload(ByteBuffer buffer)
|
||||||
{
|
{
|
||||||
payloadLength = getFrame().getPayloadLength();
|
|
||||||
while (buffer.hasRemaining())
|
while (buffer.hasRemaining())
|
||||||
{
|
{
|
||||||
if (payload == null)
|
if (payload == null)
|
||||||
|
@ -283,12 +331,4 @@ public class FrameParser
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Reset the frame and parser states
|
|
||||||
*/
|
|
||||||
public void reset() {
|
|
||||||
// reset parser
|
|
||||||
state = State.PAYLOAD_LEN;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,12 +7,9 @@ import java.util.concurrent.CopyOnWriteArrayList;
|
||||||
|
|
||||||
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.api.ProtocolException;
|
|
||||||
import org.eclipse.jetty.websocket.api.WebSocketException;
|
import org.eclipse.jetty.websocket.api.WebSocketException;
|
||||||
import org.eclipse.jetty.websocket.api.WebSocketPolicy;
|
import org.eclipse.jetty.websocket.api.WebSocketPolicy;
|
||||||
import org.eclipse.jetty.websocket.protocol.OpCode;
|
|
||||||
import org.eclipse.jetty.websocket.protocol.WebSocketFrame;
|
import org.eclipse.jetty.websocket.protocol.WebSocketFrame;
|
||||||
import org.eclipse.jetty.websocket.util.CloseUtil;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parsing of a frames in WebSocket land.
|
* Parsing of a frames in WebSocket land.
|
||||||
|
@ -26,18 +23,10 @@ public class Parser
|
||||||
public void onWebSocketException(WebSocketException e);
|
public void onWebSocketException(WebSocketException e);
|
||||||
}
|
}
|
||||||
|
|
||||||
private enum State
|
|
||||||
{
|
|
||||||
FINOP,
|
|
||||||
BASE_FRAMING,
|
|
||||||
PAYLOAD
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final Logger LOG = Log.getLogger(Parser.class);
|
private static final Logger LOG = Log.getLogger(Parser.class);
|
||||||
private final List<Listener> listeners = new CopyOnWriteArrayList<>();
|
private final List<Listener> listeners = new CopyOnWriteArrayList<>();
|
||||||
private final FrameParser parser;
|
private final FrameParser parser;
|
||||||
private WebSocketPolicy policy;
|
private WebSocketPolicy policy;
|
||||||
private State state = State.FINOP;
|
|
||||||
|
|
||||||
public Parser(WebSocketPolicy wspolicy)
|
public Parser(WebSocketPolicy wspolicy)
|
||||||
{
|
{
|
||||||
|
@ -47,7 +36,6 @@ public class Parser
|
||||||
|
|
||||||
this.policy = wspolicy;
|
this.policy = wspolicy;
|
||||||
this.parser = new FrameParser(wspolicy);
|
this.parser = new FrameParser(wspolicy);
|
||||||
reset();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addListener(Listener listener)
|
public void addListener(Listener listener)
|
||||||
|
@ -55,11 +43,6 @@ public class Parser
|
||||||
listeners.add(listener);
|
listeners.add(listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void assertValidClose()
|
|
||||||
{
|
|
||||||
CloseUtil.assertValidPayload(parser.getFrame());
|
|
||||||
}
|
|
||||||
|
|
||||||
public WebSocketPolicy getPolicy()
|
public WebSocketPolicy getPolicy()
|
||||||
{
|
{
|
||||||
return policy;
|
return policy;
|
||||||
|
@ -102,91 +85,11 @@ public class Parser
|
||||||
LOG.debug("Parsing {} bytes",buffer.remaining());
|
LOG.debug("Parsing {} bytes",buffer.remaining());
|
||||||
while (buffer.hasRemaining())
|
while (buffer.hasRemaining())
|
||||||
{
|
{
|
||||||
switch (state)
|
if (parser.parse(buffer))
|
||||||
{
|
{
|
||||||
case FINOP:
|
notifyFrame(parser.getFrame());
|
||||||
{
|
|
||||||
// peek at byte
|
|
||||||
byte b = buffer.get();
|
|
||||||
boolean fin = ((b & 0x80) != 0);
|
|
||||||
boolean rsv1 = ((b & 0x40) != 0);
|
|
||||||
boolean rsv2 = ((b & 0x20) != 0);
|
|
||||||
boolean rsv3 = ((b & 0x10) != 0);
|
|
||||||
byte opc = (byte)(b & 0x0F);
|
|
||||||
OpCode opcode = OpCode.from(opc);
|
|
||||||
|
|
||||||
if (opcode == null)
|
|
||||||
{
|
|
||||||
throw new WebSocketException("Unknown opcode: " + opc);
|
|
||||||
}
|
|
||||||
|
|
||||||
LOG.debug("OpCode {}, fin={}",opcode.name(),fin);
|
|
||||||
|
|
||||||
if (opcode.isControlFrame() && !fin)
|
|
||||||
{
|
|
||||||
throw new ProtocolException("Fragmented Control Frame [" + opcode.name() + "]");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (opcode == OpCode.CONTINUATION)
|
|
||||||
{
|
|
||||||
if (parser.getFrame() == null)
|
|
||||||
{
|
|
||||||
throw new ProtocolException("Fragment continuation frame without prior !FIN");
|
|
||||||
}
|
|
||||||
// Be careful to use the original opcode
|
|
||||||
opcode = parser.getFrame().getOpCode();
|
|
||||||
}
|
|
||||||
|
|
||||||
parser.reset();
|
|
||||||
parser.initFrame(fin,rsv1,rsv2,rsv3,opcode);
|
|
||||||
|
|
||||||
state = State.BASE_FRAMING;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case BASE_FRAMING:
|
|
||||||
{
|
|
||||||
if (parser.parseBaseFraming(buffer))
|
|
||||||
{
|
|
||||||
state = State.PAYLOAD;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case PAYLOAD:
|
|
||||||
{
|
|
||||||
if (parser.parsePayload(buffer))
|
|
||||||
{
|
|
||||||
// special check for close
|
|
||||||
if (parser.getFrame().getOpCode() == OpCode.CLOSE)
|
|
||||||
{
|
|
||||||
assertValidClose();
|
|
||||||
}
|
|
||||||
notifyFrame(parser.getFrame());
|
|
||||||
parser.reset();
|
|
||||||
if (parser.getFrame().isFin())
|
|
||||||
{
|
|
||||||
reset();
|
|
||||||
}
|
|
||||||
state = State.FINOP;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* if the payload was empty we could end up in this state because there was no remaining bits to process
|
|
||||||
*/
|
|
||||||
if (state == State.PAYLOAD)
|
|
||||||
{
|
|
||||||
notifyFrame(parser.getFrame());
|
|
||||||
parser.reset();
|
|
||||||
if (parser.getFrame().isFin())
|
|
||||||
{
|
|
||||||
reset();
|
|
||||||
}
|
|
||||||
state = State.FINOP;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
catch (WebSocketException e)
|
catch (WebSocketException e)
|
||||||
{
|
{
|
||||||
|
@ -208,15 +111,9 @@ public class Parser
|
||||||
listeners.remove(listener);
|
listeners.remove(listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void reset()
|
|
||||||
{
|
|
||||||
state = State.FINOP;
|
|
||||||
parser.reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString()
|
public String toString()
|
||||||
{
|
{
|
||||||
return String.format("%s(state=%s, parser=%s)", getClass().getSimpleName(), state, parser);
|
return String.format("%s(parser=%s)",getClass().getSimpleName(),parser);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -76,19 +76,19 @@ public class WebSocketFrame implements Frame
|
||||||
throw new ProtocolException("Cannot have FIN==false on Control frames");
|
throw new ProtocolException("Cannot have FIN==false on Control frames");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rsv1 == false)
|
if (rsv1 == true)
|
||||||
{
|
{
|
||||||
throw new ProtocolException("Cannot have RSV1==false on Control frames");
|
throw new ProtocolException("Cannot have RSV1==true on Control frames");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rsv2 == false)
|
if (rsv2 == true)
|
||||||
{
|
{
|
||||||
throw new ProtocolException("Cannot have RSV2==false on Control frames");
|
throw new ProtocolException("Cannot have RSV2==true on Control frames");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rsv3 == false)
|
if (rsv3 == true)
|
||||||
{
|
{
|
||||||
throw new ProtocolException("Cannot have RSV3==false on Control frames");
|
throw new ProtocolException("Cannot have RSV3==true on Control frames");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isContinuation())
|
if (isContinuation())
|
||||||
|
|
Loading…
Reference in New Issue