Merge branch 'jetty-9' of ssh://git.eclipse.org/gitroot/jetty/org.eclipse.jetty.project into jetty-9

This commit is contained in:
Jesse McConnell 2012-07-06 15:16:19 -05:00
commit 285701f98e
3 changed files with 93 additions and 156 deletions

View File

@ -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;
}
} }

View File

@ -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);
} }
} }

View File

@ -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())