Introduction PayloadParser and OpCodes

This commit is contained in:
Joakim Erdfelt 2012-06-18 14:24:24 -07:00
parent b9ca387d53
commit cb6968d680
25 changed files with 360 additions and 655 deletions

View File

@ -1,65 +0,0 @@
/*******************************************************************************
* Copyright (c) 2011 Intalio, Inc.
* ======================================================================
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Apache License v2.0 which accompanies this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* The Apache License v2.0 is available at
* http://www.opensource.org/licenses/apache2.0.php
*
* You may elect to redistribute this code under either of these licenses.
*******************************************************************************/
// ========================================================================
// Copyright (c) 2010 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
package org.eclipse.jetty.websocket;
import java.nio.ByteBuffer;
/* ------------------------------------------------------------ */
/**
* Parser the WebSocket protocol.
*
*/
public interface WebSocketParser
{
/* ------------------------------------------------------------ */
/* ------------------------------------------------------------ */
/* ------------------------------------------------------------ */
public interface FrameHandler
{
void onFrame(byte flags, byte opcode, ByteBuffer buffer);
void close(int code,String message);
}
ByteBuffer getBuffer();
/**
* @return an indication of progress, normally bytes filled plus events parsed, or -1 for EOF
*/
int parseNext();
boolean isBufferEmpty();
void fill(ByteBuffer buffer);
}

View File

@ -1,404 +0,0 @@
/*******************************************************************************
* Copyright (c) 2011 Intalio, Inc.
* ======================================================================
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Apache License v2.0 which accompanies this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* The Apache License v2.0 is available at
* http://www.opensource.org/licenses/apache2.0.php
*
* You may elect to redistribute this code under either of these licenses.
*******************************************************************************/
// ========================================================================
// Copyright (c) 2010 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
package org.eclipse.jetty.websocket;
import java.io.IOException;
import java.nio.ByteBuffer;
import org.eclipse.jetty.io.EndPoint;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
/* ------------------------------------------------------------ */
/**
* Parser the WebSocket protocol.
*
*/
public class WebSocketParserRFC6455 implements WebSocketParser
{
private static final Logger LOG = Log.getLogger(WebSocketParserRFC6455.class);
public enum State {
START(0), OPCODE(1), LENGTH_7(1), LENGTH_16(2), LENGTH_63(8), MASK(4), PAYLOAD(0), DATA(0), SKIP(1), SEEK_EOF(1);
int _needs;
State(int needs)
{
_needs=needs;
}
int getNeeds()
{
return _needs;
}
}
private final WebSocketBuffers _buffers;
private final EndPoint _endp;
private final FrameHandler _handler;
private final boolean _shouldBeMasked;
private State _state;
private ByteBuffer _buffer;
private byte _flags;
private byte _opcode;
private int _bytesNeeded;
private long _length;
private boolean _masked;
private final byte[] _mask = new byte[4];
private int _m;
private boolean _skip;
private boolean _fragmentFrames=true;
/* ------------------------------------------------------------ */
/**
* @param buffers The buffers to use for parsing. Only the {@link Buffers#getData()} is used.
* This should be a direct buffer if binary data is mostly used or an indirect buffer if utf-8 data
* is mostly used.
* @param endp the endpoint
* @param handler the handler to notify when a parse event occurs
* @param shouldBeMasked whether masking should be handled
*/
public WebSocketParserRFC6455(WebSocketBuffers buffers, EndPoint endp, FrameHandler handler, boolean shouldBeMasked)
{
_buffers=buffers;
_endp=endp;
_handler=handler;
_shouldBeMasked=shouldBeMasked;
_state=State.START;
}
/* ------------------------------------------------------------ */
/**
* @return True if fake fragments should be created for frames larger than the buffer.
*/
public boolean isFakeFragments()
{
return _fragmentFrames;
}
/* ------------------------------------------------------------ */
/**
* @param fakeFragments True if fake fragments should be created for frames larger than the buffer.
*/
public void setFakeFragments(boolean fakeFragments)
{
_fragmentFrames = fakeFragments;
}
/* ------------------------------------------------------------ */
public boolean isBufferEmpty()
{
return _buffer==null || _buffer.length()==0;
}
/* ------------------------------------------------------------ */
public ByteBuffer getBuffer()
{
return _buffer;
}
/* ------------------------------------------------------------ */
/** Parse to next event.
* Parse to the next {@link WebSocketParser.FrameHandler} event or until no more data is
* available. Fill data from the {@link EndPoint} only as necessary.
* @return An indication of progress or otherwise. -1 indicates EOF, 0 indicates
* that no bytes were read and no messages parsed. A positive number indicates either
* the bytes filled or the messages parsed.
*/
public int parseNext()
{
if (_buffer==null)
_buffer=_buffers.getData();
boolean progress=false;
int filled=-1;
// Loop until a datagram call back or can't fill anymore
while(!progress && (!_endp.isInputShutdown()||_buffer.length()>0))
{
int available=_buffer.length();
// Fill buffer if we need a byte or need length
while (available<(_state==State.SKIP?1:_bytesNeeded))
{
// compact to mark (set at start of data)
_buffer.compact();
// if no space, then the data is too big for buffer
if (_buffer.space() == 0)
{
// Can we send a fake frame?
if (_fragmentFrames && _state==State.DATA)
{
ByteBuffer data =_buffer.get(4*(available/4));
_buffer.compact();
if (_masked)
{
if (data.array()==null)
data=_buffer.asMutableBuffer();
byte[] array = data.array();
final int end=data.putIndex();
for (int i=data.getIndex();i<end;i++)
array[i]^=_mask[_m++%4];
}
// System.err.printf("%s %s %s >>\n",TypeUtil.toHexString(_flags),TypeUtil.toHexString(_opcode),data.length());
_bytesNeeded-=data.length();
progress=true;
_handler.onFrame((byte)(_flags&(0xff^WebSocketConnectionRFC6455.FLAG_FIN)), _opcode, data);
_opcode=WebSocketConnectionRFC6455.OP_CONTINUATION;
}
if (_buffer.space() == 0)
throw new IllegalStateException("FULL: "+_state+" "+_bytesNeeded+">"+_buffer.capacity());
}
// catch IOExceptions (probably EOF) and try to parse what we have
try
{
filled=_endp.isInputShutdown()?-1:_endp.fill(_buffer);
available=_buffer.length();
// System.err.printf(">> filled %d/%d%n",filled,available);
if (filled<=0)
break;
}
catch(IOException e)
{
LOG.debug(e);
filled=-1;
break;
}
}
// Did we get enough?
if (available<(_state==State.SKIP?1:_bytesNeeded))
break;
// if we are here, then we have sufficient bytes to process the current state.
// Parse the buffer byte by byte (unless it is STATE_DATA)
byte b;
while (_state!=State.DATA && available>=(_state==State.SKIP?1:_bytesNeeded))
{
switch (_state)
{
case START:
_skip=false;
_state=_opcode==WebSocketConnectionRFC6455.OP_CLOSE?State.SEEK_EOF:State.OPCODE;
_bytesNeeded=_state.getNeeds();
continue;
case OPCODE:
b=_buffer.get();
available--;
_opcode=(byte)(b&0xf);
_flags=(byte)(0xf&(b>>4));
if (WebSocketConnectionRFC6455.isControlFrame(_opcode)&&!WebSocketConnectionRFC6455.isLastFrame(_flags))
{
LOG.warn("Fragmented Control from "+_endp);
_handler.close(WebSocketConnectionRFC6455.CLOSE_PROTOCOL,"Fragmented control");
progress=true;
_skip=true;
}
_state=State.LENGTH_7;
_bytesNeeded=_state.getNeeds();
continue;
case LENGTH_7:
b=_buffer.get();
available--;
_masked=(b&0x80)!=0;
b=(byte)(0x7f&b);
switch(b)
{
case 0x7f:
_length=0;
_state=State.LENGTH_63;
break;
case 0x7e:
_length=0;
_state=State.LENGTH_16;
break;
default:
_length=(0x7f&b);
_state=_masked?State.MASK:State.PAYLOAD;
}
_bytesNeeded=_state.getNeeds();
continue;
case LENGTH_16:
b=_buffer.get();
available--;
_length = _length*0x100 + (0xff&b);
if (--_bytesNeeded==0)
{
if (_length>_buffer.capacity() && !_fragmentFrames)
{
progress=true;
_handler.close(WebSocketConnectionRFC6455.CLOSE_POLICY_VIOLATION,"frame size "+_length+">"+_buffer.capacity());
_skip=true;
}
_state=_masked?State.MASK:State.PAYLOAD;
_bytesNeeded=_state.getNeeds();
}
continue;
case LENGTH_63:
b=_buffer.get();
available--;
_length = _length*0x100 + (0xff&b);
if (--_bytesNeeded==0)
{
_bytesNeeded=(int)_length;
if (_length>=_buffer.capacity() && !_fragmentFrames)
{
progress=true;
_handler.close(WebSocketConnectionRFC6455.CLOSE_POLICY_VIOLATION,"frame size "+_length+">"+_buffer.capacity());
_skip=true;
}
_state=_masked?State.MASK:State.PAYLOAD;
_bytesNeeded=_state.getNeeds();
}
continue;
case MASK:
_buffer.get(_mask,0,4);
_m=0;
available-=4;
_state=State.PAYLOAD;
_bytesNeeded=_state.getNeeds();
break;
case PAYLOAD:
_bytesNeeded=(int)_length;
_state=_skip?State.SKIP:State.DATA;
break;
case DATA:
break;
case SKIP:
int skip=Math.min(available,_bytesNeeded);
progress=true;
_buffer.skip(skip);
available-=skip;
_bytesNeeded-=skip;
if (_bytesNeeded==0)
_state=State.START;
break;
case SEEK_EOF:
progress=true;
_buffer.skip(available);
available=0;
break;
}
}
if (_state==State.DATA && available>=_bytesNeeded)
{
if ( _masked!=_shouldBeMasked)
{
_buffer.skip(_bytesNeeded);
_state=State.START;
progress=true;
_handler.close(WebSocketConnectionRFC6455.CLOSE_PROTOCOL,"Not masked");
}
else
{
ByteBuffer data =_buffer.get(_bytesNeeded);
if (_masked)
{
if (data.array()==null)
data=_buffer.asMutableBuffer();
byte[] array = data.array();
final int end=data.putIndex();
for (int i=data.getIndex();i<end;i++)
array[i]^=_mask[_m++%4];
}
// System.err.printf("%s %s %s >>\n",TypeUtil.toHexString(_flags),TypeUtil.toHexString(_opcode),data.length());
progress=true;
_handler.onFrame(_flags, _opcode, data);
_bytesNeeded=0;
_state=State.START;
}
break;
}
}
return progress?1:filled;
}
/* ------------------------------------------------------------ */
public void fill(ByteBuffer buffer)
{
if (buffer!=null && buffer.length()>0)
{
if (_buffer==null)
_buffer=_buffers.getData();
_buffer.put(buffer);
buffer.clear();
}
}
/* ------------------------------------------------------------ */
public void returnBuffer()
{
if (_buffer!=null && _buffer.length()==0)
{
_buffers.returnBuffer(_buffer);
_buffer=null;
}
}
/* ------------------------------------------------------------ */
@Override
public String toString()
{
return String.format("%s@%x state=%s buffer=%s",
getClass().getSimpleName(),
hashCode(),
_state,
_buffer);
}
}

View File

@ -0,0 +1,89 @@
package org.eclipse.jetty.websocket.api;
import java.util.HashMap;
import java.util.Map;
import org.eclipse.jetty.websocket.frames.BinaryFrame;
import org.eclipse.jetty.websocket.frames.CloseFrame;
import org.eclipse.jetty.websocket.frames.ContinuationFrame;
import org.eclipse.jetty.websocket.frames.PingFrame;
import org.eclipse.jetty.websocket.frames.PongFrame;
import org.eclipse.jetty.websocket.frames.TextFrame;
public enum OpCode
{
/**
* OpCode for a {@link ContinuationFrame}
*
* @see <a href="https://tools.ietf.org/html/rfc6455#section-11.8">RFC 6455, Section 11.8 (WebSocket Opcode Registry</a>
*/
CONTINUATION((byte)0x00),
/**
* OpCode for a {@link TextFrame}
*
* @see <a href="https://tools.ietf.org/html/rfc6455#section-11.8">RFC 6455, Section 11.8 (WebSocket Opcode Registry</a>
*/
TEXT((byte)0x01),
/**
* OpCode for a {@link BinaryFrame}
*
* @see <a href="https://tools.ietf.org/html/rfc6455#section-11.8">RFC 6455, Section 11.8 (WebSocket Opcode Registry</a>
*/
BINARY((byte)0x02),
/**
* OpCode for a {@link CloseFrame}
*
* @see <a href="https://tools.ietf.org/html/rfc6455#section-11.8">RFC 6455, Section 11.8 (WebSocket Opcode Registry</a>
*/
CLOSE((byte)0x08),
/**
* OpCode for a {@link PingFrame}
*
* @see <a href="https://tools.ietf.org/html/rfc6455#section-11.8">RFC 6455, Section 11.8 (WebSocket Opcode Registry</a>
*/
PING((byte)0x09),
/**
* OpCode for a {@link PongFrame}
*
* @see <a href="https://tools.ietf.org/html/rfc6455#section-11.8">RFC 6455, Section 11.8 (WebSocket Opcode Registry</a>
*/
PONG((byte)0x0A);
private static class Codes
{
private static final Map<Byte, OpCode> codes = new HashMap<>();
}
/**
* Get OpCode from specified value.
*
* @param opcode
* @return
*/
public static OpCode from(byte opcode)
{
return Codes.codes.get(opcode);
}
private byte opcode;
private OpCode(byte opcode)
{
this.opcode = opcode;
Codes.codes.put(opcode,this);
}
public byte getCode()
{
return this.opcode;
}
public boolean isControlFrame() {
return (opcode >= CLOSE.opcode);
}
}

View File

@ -1,5 +1,7 @@
package org.eclipse.jetty.websocket.frames;
import org.eclipse.jetty.websocket.api.OpCode;
/**
* A Base Frame as seen in <a href="https://tools.ietf.org/html/rfc6455#section-5.2">RFC 6455. Sec 5.2</a>
*
@ -26,59 +28,24 @@ package org.eclipse.jetty.websocket.frames;
*/
public class BaseFrame
{
/**
* OpCode for a {@link ContinuationFrame}
*
* @see <a href="https://tools.ietf.org/html/rfc6455#section-11.8">RFC 6455, Section 11.8 (WebSocket Opcode Registry</a>
*/
public final static byte OP_CONTINUATION = 0x00;
/**
* OpCode for a {@link TextFrame}
*
* @see <a href="https://tools.ietf.org/html/rfc6455#section-11.8">RFC 6455, Section 11.8 (WebSocket Opcode Registry</a>
*/
public final static byte OP_TEXT = 0x01;
/**
* OpCode for a {@link BinaryFrame}
*
* @see <a href="https://tools.ietf.org/html/rfc6455#section-11.8">RFC 6455, Section 11.8 (WebSocket Opcode Registry</a>
*/
public final static byte OP_BINARY = 0x02;
/**
* OpCode for a {@link CloseFrame}
*
* @see <a href="https://tools.ietf.org/html/rfc6455#section-11.8">RFC 6455, Section 11.8 (WebSocket Opcode Registry</a>
*/
public final static byte OP_CLOSE = 0x08;
/**
* OpCode for a {@link PingFrame}
*
* @see <a href="https://tools.ietf.org/html/rfc6455#section-11.8">RFC 6455, Section 11.8 (WebSocket Opcode Registry</a>
*/
public final static byte OP_PING = 0x09;
/**
* OpCode for a {@link PongFrame}
*
* @see <a href="https://tools.ietf.org/html/rfc6455#section-11.8">RFC 6455, Section 11.8 (WebSocket Opcode Registry</a>
*/
public final static byte OP_PONG = 0x0A;
private boolean fin;
private boolean rsv1;
private boolean rsv2;
private boolean rsv3;
private byte opcode = -1;
private OpCode opcode = null;
private boolean masked = false;
private long payloadLength;
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
*/
public BaseFrame() {
/* default */
reset();
}
/**
@ -86,6 +53,7 @@ public class BaseFrame
* @param copy the copy
*/
public BaseFrame(BaseFrame copy) {
this();
this.fin = copy.fin;
this.rsv1 = copy.rsv1;
this.rsv2 = copy.rsv2;
@ -100,6 +68,14 @@ public class BaseFrame
}
}
/**
* Construct form opcode
*/
public BaseFrame(OpCode opcode) {
reset();
this.opcode = opcode;
}
public byte[] getMask()
{
if (!masked)
@ -109,21 +85,16 @@ public class BaseFrame
return mask;
}
public byte getOpcode()
public OpCode getOpCode()
{
return opcode;
}
public long getPayloadLength()
public int getPayloadLength()
{
return payloadLength;
}
public boolean isControlFrame()
{
return (opcode >= OP_CLOSE);
}
public boolean isFin()
{
return fin;
@ -154,6 +125,18 @@ public class BaseFrame
return rsv3;
}
public void reset()
{
fin = false;
rsv1 = false;
rsv2 = false;
rsv3 = false;
opcode = null;
masked = false;
payloadLength = -1;
mask = null;
}
public void setFin(boolean fin)
{
this.fin = fin;
@ -169,12 +152,12 @@ public class BaseFrame
this.masked = mask;
}
public void setOpcode(byte opcode)
public void setOpCode(OpCode opCode)
{
this.opcode = opcode;
this.opcode = opCode;
}
public void setPayloadLength(long payloadLength)
public void setPayloadLength(int payloadLength)
{
this.payloadLength = payloadLength;
}

View File

@ -2,10 +2,12 @@ package org.eclipse.jetty.websocket.frames;
import java.nio.ByteBuffer;
import org.eclipse.jetty.websocket.api.OpCode;
/**
* Representation of a <a href="https://tools.ietf.org/html/rfc6455#section-5.6">Binary Data Frame (0x02)</a>.
*/
public class BinaryFrame extends BaseFrame
public class BinaryFrame extends DataFrame
{
private ByteBuffer data; // TODO: make this a standard byte buffer?
@ -14,8 +16,7 @@ public class BinaryFrame extends BaseFrame
*/
public BinaryFrame()
{
super();
super.setOpcode(OP_BINARY);
super(OpCode.BINARY);
}
/**
@ -29,7 +30,7 @@ public class BinaryFrame extends BaseFrame
super(base);
// TODO: limit this somehow?
// TODO: create a streaming binary frame?
data = ByteBuffer.allocate((int)base.getPayloadLength());
data = ByteBuffer.allocate(base.getPayloadLength());
}
/**
@ -42,6 +43,12 @@ public class BinaryFrame extends BaseFrame
return data;
}
@Override
public OpCode getOpCode()
{
return OpCode.BINARY;
}
/**
* Set the data and payload length.
*

View File

@ -1,5 +1,6 @@
package org.eclipse.jetty.websocket.frames;
import org.eclipse.jetty.websocket.api.OpCode;
import org.eclipse.jetty.websocket.api.WebSocket;
/**
@ -17,10 +18,16 @@ public class CloseFrame extends ControlFrame
public CloseFrame(short statusCode)
{
super(ControlFrameType.CLOSE_FRAME);
super(OpCode.CLOSE);
this.statusCode = statusCode;
}
@Override
public OpCode getOpCode()
{
return OpCode.CLOSE;
}
public String getReason()
{
return reason;

View File

@ -1,5 +1,11 @@
package org.eclipse.jetty.websocket.frames;
public class ContinuationFrame {
import org.eclipse.jetty.websocket.api.OpCode;
public class ContinuationFrame extends BaseFrame
{
public ContinuationFrame()
{
super(OpCode.CONTINUATION);
}
}

View File

@ -1,27 +1,21 @@
package org.eclipse.jetty.websocket.frames;
/**
* Base class for all <a href="https://tools.ietf.org/html/rfc6455#section-5.5">control frames</a>.
* <p>
* TODO: investigate as candidate for removal.
*/
import org.eclipse.jetty.websocket.api.OpCode;
public abstract class ControlFrame extends BaseFrame
{
private final ControlFrameType type;
public ControlFrame(ControlFrameType type)
public ControlFrame()
{
this.type = type;
super();
}
public ControlFrameType getType()
public ControlFrame(BaseFrame copy)
{
return type;
super(copy);
}
@Override
public String toString()
public ControlFrame(OpCode opcode)
{
return String.format("%s frame v%s",getType());
super(opcode);
}
}

View File

@ -1,50 +0,0 @@
/*
* Copyright (c) 2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.eclipse.jetty.websocket.frames;
import java.util.HashMap;
import java.util.Map;
public enum ControlFrameType
{
PING_FRAME(BaseFrame.OP_PING),
PONG_FRAME(BaseFrame.OP_PONG),
CLOSE_FRAME(BaseFrame.OP_CLOSE);
private static class Codes
{
private static final Map<Byte, ControlFrameType> codes = new HashMap<>();
}
public static ControlFrameType from(byte opcode)
{
return Codes.codes.get(opcode);
}
private final byte opcode;
private ControlFrameType(byte opcode)
{
this.opcode = opcode;
Codes.codes.put(opcode,this);
}
public short getCode()
{
return opcode;
}
}

View File

@ -0,0 +1,21 @@
package org.eclipse.jetty.websocket.frames;
import org.eclipse.jetty.websocket.api.OpCode;
public abstract class DataFrame extends BaseFrame
{
public DataFrame()
{
super();
}
public DataFrame(BaseFrame copy)
{
super(copy);
}
public DataFrame(OpCode opcode)
{
super(opcode);
}
}

View File

@ -1,5 +1,7 @@
package org.eclipse.jetty.websocket.frames;
import org.eclipse.jetty.websocket.api.OpCode;
/**
* Representation of a <a href="https://tools.ietf.org/html/rfc6455#section-5.5.2">Ping Frame (0x09)</a>.
*/
@ -9,10 +11,16 @@ public class PingFrame extends ControlFrame
public PingFrame(int pingId)
{
super(ControlFrameType.PING_FRAME);
super(OpCode.PING);
this.pingId = pingId;
}
@Override
public OpCode getOpCode()
{
return OpCode.PING;
}
public int getPingId()
{
return pingId;

View File

@ -1,5 +1,7 @@
package org.eclipse.jetty.websocket.frames;
import org.eclipse.jetty.websocket.api.OpCode;
/**
* Representation of a <a href="https://tools.ietf.org/html/rfc6455#section-5.5.3">Pong Frame (0x0A)</a>.
*/
@ -9,7 +11,7 @@ public class PongFrame extends ControlFrame
public PongFrame(int pingId)
{
super(ControlFrameType.PONG_FRAME);
super(OpCode.PONG);
this.pingId = pingId;
}

View File

@ -1,9 +1,11 @@
package org.eclipse.jetty.websocket.frames;
import org.eclipse.jetty.websocket.api.OpCode;
/**
* Representation of a <a href="https://tools.ietf.org/html/rfc6455#section-5.6">Text Data Frame (0x01)</a>.
*/
public class TextFrame extends BaseFrame
public class TextFrame extends DataFrame
{
private StringBuilder data = new StringBuilder();
@ -12,8 +14,7 @@ public class TextFrame extends BaseFrame
*/
public TextFrame()
{
super();
super.setOpcode(OP_TEXT);
super(OpCode.TEXT);
}
/**

View File

@ -4,11 +4,8 @@ import java.nio.ByteBuffer;
import java.util.EnumMap;
import org.eclipse.jetty.io.ByteBufferPool;
import org.eclipse.jetty.websocket.frames.BaseFrame;
import org.eclipse.jetty.websocket.api.OpCode;
import org.eclipse.jetty.websocket.frames.ControlFrame;
import org.eclipse.jetty.websocket.frames.ControlFrameType;
import org.eclipse.jetty.websocket.frames.PingFrame;
import org.eclipse.jetty.websocket.generator.ControlFrameGenerator;
/**
* Generating a frame in WebSocket land.
@ -36,22 +33,22 @@ import org.eclipse.jetty.websocket.generator.ControlFrameGenerator;
*/
public class Generator {
private final EnumMap<ControlFrameType, ControlFrameGenerator> generators = new EnumMap<>(ControlFrameType.class);
private final EnumMap<OpCode, ControlFrameGenerator> generators = new EnumMap<>(OpCode.class);
public Generator(ByteBufferPool bufferPool) //, CompressionFactory.Compressor compressor)
{
HeadersBlockGenerator headerBlockGenerator = new HeadersBlockGenerator();
generators.put(ControlFrameType.PING_FRAME, new PingFrameGenerator());
generators.put(ControlFrameType.PONG_FRAME, new PongFrameGenerator());
generators.put(ControlFrameType.CLOSE_FRAME, new CloseFrameGenerator());
generators.put(OpCode.PING,new PingFrameGenerator());
generators.put(OpCode.PONG,new PongFrameGenerator());
generators.put(OpCode.CLOSE,new CloseFrameGenerator());
}
public ByteBuffer control(ControlFrame frame)
{
ControlFrameGenerator generator = generators.get(frame.getType());
ControlFrameGenerator generator = generators.get(frame.getOpCode());
return generator.generate(frame);
}

View File

@ -0,0 +1,15 @@
package org.eclipse.jetty.websocket.parser;
import java.nio.ByteBuffer;
public class BinaryPayloadParser extends PayloadParser
{
@Override
public boolean parse(ByteBuffer buffer)
{
// TODO Auto-generated method stub
return false;
}
}

View File

@ -0,0 +1,15 @@
package org.eclipse.jetty.websocket.parser;
import java.nio.ByteBuffer;
public class ClosePayloadParser extends PayloadParser
{
@Override
public boolean parse(ByteBuffer buffer)
{
// TODO Auto-generated method stub
return false;
}
}

View File

@ -0,0 +1,15 @@
package org.eclipse.jetty.websocket.parser;
import java.nio.ByteBuffer;
public class ContinuationPayloadParser extends PayloadParser
{
@Override
public boolean parse(ByteBuffer buffer)
{
// TODO Auto-generated method stub
return false;
}
}

View File

@ -1,5 +0,0 @@
package org.eclipse.jetty.websocket.parser;
public class ControlFrameBodyParser {
}

View File

@ -1,5 +0,0 @@
package org.eclipse.jetty.websocket.parser;
public class ControlFrameParser {
}

View File

@ -1,17 +1,18 @@
package org.eclipse.jetty.websocket.parser;
import java.nio.ByteBuffer;
import java.util.EnumMap;
import java.util.EventListener;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
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.frames.BaseFrame;
import org.eclipse.jetty.websocket.frames.BinaryFrame;
import org.eclipse.jetty.websocket.frames.ControlFrame;
import org.eclipse.jetty.websocket.frames.TextFrame;
import org.eclipse.jetty.websocket.frames.DataFrame;
/**
* Parsing of a frame in WebSocket land.
@ -42,18 +43,13 @@ public class Parser {
{
public static class Adapter implements Listener
{
@Override
public void onBinaryFrame(final BinaryFrame frame)
{
}
@Override
public void onControlFrame(final ControlFrame frame)
{
}
@Override
public void onTextFrame(final TextFrame frame)
public void onDataFrame(final DataFrame frame)
{
}
@ -63,25 +59,32 @@ public class Parser {
}
}
public void onBinaryFrame(final BinaryFrame frame);
public void onControlFrame(final ControlFrame frame);
public void onTextFrame(final TextFrame frame);
public void onDataFrame(final DataFrame frame);
public void onWebSocketException(WebSocketException e);
}
private enum State
{
FINOP, PAYLOAD_LEN, PAYLOAD_LEN_BYTES, MASK, MASK_BYTES, PAYLOAD
FINOP,
PAYLOAD_LEN,
PAYLOAD_LEN_BYTES,
MASK,
MASK_BYTES,
PAYLOAD
}
private static final Logger LOG = Log.getLogger(Parser.class);
private final List<Listener> listeners = new CopyOnWriteArrayList<>();
private State state = State.FINOP;
private final EnumMap<OpCode, PayloadParser> parsers = new EnumMap<>(OpCode.class);
// Holder for the values represented in the baseframe being parsed.
private BaseFrame baseframe = new BaseFrame();
private long length = 0;
private int length = 0;
private int cursor = 0;
private PayloadParser parser;
public Parser()
{
@ -89,7 +92,14 @@ public class Parser {
* TODO: Investigate addition of decompression factory similar to SPDY work in situation of negotiated deflate extension?
*/
baseframe = new BaseFrame();
state = State.FINOP;
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());
}
public void addListener(Listener listener)
@ -97,21 +107,6 @@ public class Parser {
listeners.add(listener);
}
protected void notifyBinaryFrame(final BinaryFrame frame) {
LOG.debug("Notify Binary Frame: {}",frame);
for (Listener listener : listeners)
{
try
{
listener.onBinaryFrame(frame);
}
catch (Throwable t)
{
LOG.warn(t);
}
}
}
protected void notifyControlFrame(final ControlFrame f)
{
LOG.debug("Notify Control Frame: {}",f);
@ -128,13 +123,13 @@ public class Parser {
}
}
protected void notifyTextFrame(final TextFrame frame) {
LOG.debug("Notify Text Frame: {}",frame);
protected void notifyDataFrame(final DataFrame frame) {
LOG.debug("Notify Data Frame: {}",frame);
for (Listener listener : listeners)
{
try
{
listener.onTextFrame(frame);
listener.onDataFrame(frame);
}
catch (Throwable t)
{
@ -169,9 +164,10 @@ public class Parser {
baseframe.setRsv1((flags & BaseFrame.FLAG_RSV1) == 1);
baseframe.setRsv2((flags & BaseFrame.FLAG_RSV2) == 1);
baseframe.setRsv3((flags & BaseFrame.FLAG_RSV3) == 1);
baseframe.setOpcode((byte)(b & 0xF));
OpCode opcode = OpCode.from((byte)(b & 0xF));
baseframe.setOpCode(opcode);
if (baseframe.isControlFrame() && !baseframe.isLastFrame())
if (opcode.isControlFrame() && !baseframe.isLastFrame())
{
throw new WebSocketException("Fragmented Control Frame");
}
@ -275,8 +271,16 @@ public class Parser {
}
case PAYLOAD:
{
// TODO: establish specific type parser and hand off to them.
if (parser == null)
{
// Establish specific type parser and hand off to them.
parser = parsers.get(baseframe.getOpCode());
}
if (parser.parse(buffer))
{
reset();
}
break;
}
}
@ -299,8 +303,10 @@ public class Parser {
listeners.remove(listener);
}
private void reset()
public void reset()
{
state = State.FINOP;
parser = null;
baseframe.reset();
}
}

View File

@ -0,0 +1,8 @@
package org.eclipse.jetty.websocket.parser;
import java.nio.ByteBuffer;
public abstract class PayloadParser
{
public abstract boolean parse(ByteBuffer buffer);
}

View File

@ -0,0 +1,15 @@
package org.eclipse.jetty.websocket.parser;
import java.nio.ByteBuffer;
public class PingPayloadParser extends PayloadParser
{
@Override
public boolean parse(ByteBuffer buffer)
{
// TODO Auto-generated method stub
return false;
}
}

View File

@ -0,0 +1,15 @@
package org.eclipse.jetty.websocket.parser;
import java.nio.ByteBuffer;
public class PongPayloadParser extends PayloadParser
{
@Override
public boolean parse(ByteBuffer buffer)
{
// TODO Auto-generated method stub
return false;
}
}

View File

@ -0,0 +1,15 @@
package org.eclipse.jetty.websocket.parser;
import java.nio.ByteBuffer;
public class TextPayloadParser extends PayloadParser
{
@Override
public boolean parse(ByteBuffer buffer)
{
// TODO Auto-generated method stub
return false;
}
}

View File

@ -0,0 +1,15 @@
package org.eclipse.jetty.websocket.parser;
import java.nio.ByteBuffer;
public class UnknownPayloadParser extends PayloadParser
{
@Override
public boolean parse(ByteBuffer buffer)
{
// TODO Auto-generated method stub
return false;
}
}