/jetty-8.1.5.v20120716/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketParserD08.java
Java | 388 lines | 266 code | 50 blank | 72 comment | 35 complexity | 13667ac0697ee341118c9b84e4ec1c4b MD5 | raw file
Possible License(s): Apache-2.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 org.eclipse.jetty.io.Buffer;
- import org.eclipse.jetty.io.Buffers;
- 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 WebSocketParserD08 implements WebSocketParser
- {
- private static final Logger LOG = Log.getLogger(WebSocketParserD08.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);
- 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 Buffer _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 _fakeFragments=true;
- /* ------------------------------------------------------------ */
- /**
- * @param buffers The buffers to use for parsing. Only the {@link Buffers#getBuffer()} 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 WebSocketParserD08(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 _fakeFragments;
- }
- /* ------------------------------------------------------------ */
- /**
- * @param fakeFragments True if fake fragments should be created for frames larger than the buffer.
- */
- public void setFakeFragments(boolean fakeFragments)
- {
- _fakeFragments = fakeFragments;
- }
- /* ------------------------------------------------------------ */
- public boolean isBufferEmpty()
- {
- return _buffer==null || _buffer.length()==0;
- }
- /* ------------------------------------------------------------ */
- public Buffer 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.getBuffer();
- int total_filled=0;
- int events=0;
- // Loop until a datagram call back or can't fill anymore
- while(true)
- {
- 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 (_fakeFragments && _state==State.DATA)
- {
- Buffer 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());
- events++;
- _bytesNeeded-=data.length();
- _handler.onFrame((byte)(_flags&(0xff^WebSocketConnectionD08.FLAG_FIN)), _opcode, data);
- _opcode=WebSocketConnectionD08.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
- {
- int filled=_endp.isOpen()?_endp.fill(_buffer):-1;
- if (filled<=0)
- return (total_filled+events)>0?(total_filled+events):filled;
- total_filled+=filled;
- available=_buffer.length();
- }
- catch(IOException e)
- {
- LOG.debug(e);
- return (total_filled+events)>0?(total_filled+events):-1;
- }
- }
- // 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=State.OPCODE;
- _bytesNeeded=_state.getNeeds();
- continue;
- case OPCODE:
- b=_buffer.get();
- available--;
- _opcode=(byte)(b&0xf);
- _flags=(byte)(0xf&(b>>4));
- if (WebSocketConnectionD08.isControlFrame(_opcode)&&!WebSocketConnectionD08.isLastFrame(_flags))
- {
- events++;
- LOG.warn("Fragmented Control from "+_endp);
- _handler.close(WebSocketConnectionD08.CLOSE_PROTOCOL,"Fragmented control");
- _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() && !_fakeFragments)
- {
- events++;
- _handler.close(WebSocketConnectionD08.CLOSE_BADDATA,"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() && !_fakeFragments)
- {
- events++;
- _handler.close(WebSocketConnectionD08.CLOSE_BADDATA,"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);
- _buffer.skip(skip);
- available-=skip;
- _bytesNeeded-=skip;
- if (_bytesNeeded==0)
- _state=State.START;
- }
- }
- if (_state==State.DATA && available>=_bytesNeeded)
- {
- if ( _masked!=_shouldBeMasked)
- {
- _buffer.skip(_bytesNeeded);
- _state=State.START;
- events++;
- _handler.close(WebSocketConnectionD08.CLOSE_PROTOCOL,"bad mask");
- }
- else
- {
- Buffer 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());
- events++;
- _handler.onFrame(_flags, _opcode, data);
- _bytesNeeded=0;
- _state=State.START;
- }
- return total_filled+events;
- }
- }
- }
- /* ------------------------------------------------------------ */
- public void fill(Buffer buffer)
- {
- if (buffer!=null && buffer.length()>0)
- {
- if (_buffer==null)
- _buffer=_buffers.getBuffer();
- _buffer.put(buffer);
- buffer.clear();
- }
- }
- /* ------------------------------------------------------------ */
- public void returnBuffer()
- {
- if (_buffer!=null && _buffer.length()==0)
- {
- _buffers.returnBuffer(_buffer);
- _buffer=null;
- }
- }
- /* ------------------------------------------------------------ */
- @Override
- public String toString()
- {
- Buffer buffer=_buffer;
- return WebSocketParserD08.class.getSimpleName()+"@"+ Integer.toHexString(hashCode())+"|"+_state+"|"+(buffer==null?"<>":buffer.toDetailString());
- }
- }