Home | History | Annotate | Download | only in websocket
      1 //
      2 //  ========================================================================
      3 //  Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
      4 //  ------------------------------------------------------------------------
      5 //  All rights reserved. This program and the accompanying materials
      6 //  are made available under the terms of the Eclipse Public License v1.0
      7 //  and Apache License v2.0 which accompanies this distribution.
      8 //
      9 //      The Eclipse Public License is available at
     10 //      http://www.eclipse.org/legal/epl-v10.html
     11 //
     12 //      The Apache License v2.0 is available at
     13 //      http://www.opensource.org/licenses/apache2.0.php
     14 //
     15 //  You may elect to redistribute this code under either of these licenses.
     16 //  ========================================================================
     17 //
     18 
     19 package org.eclipse.jetty.websocket;
     20 
     21 import java.io.IOException;
     22 
     23 import org.eclipse.jetty.io.Buffer;
     24 import org.eclipse.jetty.io.Buffers;
     25 import org.eclipse.jetty.io.EndPoint;
     26 import org.eclipse.jetty.util.TypeUtil;
     27 import org.eclipse.jetty.util.log.Log;
     28 import org.eclipse.jetty.util.log.Logger;
     29 
     30 
     31 
     32 
     33 /* ------------------------------------------------------------ */
     34 /**
     35  * Parser the WebSocket protocol.
     36  *
     37  */
     38 public class WebSocketParserD00 implements WebSocketParser
     39 {
     40     private static final Logger LOG = Log.getLogger(WebSocketParserD00.class);
     41 
     42     public static final int STATE_START=0;
     43     public static final int STATE_SENTINEL_DATA=1;
     44     public static final int STATE_LENGTH=2;
     45     public static final int STATE_DATA=3;
     46 
     47     private final WebSocketBuffers _buffers;
     48     private final EndPoint _endp;
     49     private final FrameHandler _handler;
     50     private int _state;
     51     private Buffer _buffer;
     52     private byte _opcode;
     53     private int _length;
     54 
     55     /* ------------------------------------------------------------ */
     56     /**
     57      * @param buffers The buffers to use for parsing.  Only the {@link Buffers#getBuffer()} is used.
     58      * This should be a direct buffer if binary data is mostly used or an indirect buffer if utf-8 data
     59      * is mostly used.
     60      * @param endp the endpoint
     61      * @param handler the handler to notify when a parse event occurs
     62      */
     63     public WebSocketParserD00(WebSocketBuffers buffers, EndPoint endp, FrameHandler handler)
     64     {
     65         _buffers=buffers;
     66         _endp=endp;
     67         _handler=handler;
     68     }
     69 
     70     /* ------------------------------------------------------------ */
     71     public boolean isBufferEmpty()
     72     {
     73         return _buffer==null || _buffer.length()==0;
     74     }
     75 
     76     /* ------------------------------------------------------------ */
     77     public Buffer getBuffer()
     78     {
     79         return _buffer;
     80     }
     81 
     82     /* ------------------------------------------------------------ */
     83     /** Parse to next event.
     84      * Parse to the next {@link WebSocketParser.FrameHandler} event or until no more data is
     85      * available. Fill data from the {@link EndPoint} only as necessary.
     86      * @return An indication of progress or otherwise. -1 indicates EOF, 0 indicates
     87      * that no bytes were read and no messages parsed. A positive number indicates either
     88      * the bytes filled or the messages parsed.
     89      */
     90     public int parseNext()
     91     {
     92         if (_buffer==null)
     93             _buffer=_buffers.getBuffer();
     94 
     95         int progress=0;
     96 
     97         // Loop until an datagram call back or can't fill anymore
     98         while(true)
     99         {
    100             int length=_buffer.length();
    101 
    102             // Fill buffer if we need a byte or need length
    103             if (length == 0 || _state==STATE_DATA && length<_length)
    104             {
    105                 // compact to mark (set at start of data)
    106                 _buffer.compact();
    107 
    108                 // if no space, then the data is too big for buffer
    109                 if (_buffer.space() == 0)
    110                     throw new IllegalStateException("FULL");
    111 
    112                 // catch IOExceptions (probably EOF) and try to parse what we have
    113                 try
    114                 {
    115                     int filled=_endp.isOpen()?_endp.fill(_buffer):-1;
    116                     if (filled<=0)
    117                         return progress;
    118                     progress+=filled;
    119                     length=_buffer.length();
    120                 }
    121                 catch(IOException e)
    122                 {
    123                     LOG.debug(e);
    124                     return progress>0?progress:-1;
    125                 }
    126             }
    127 
    128 
    129             // Parse the buffer byte by byte (unless it is STATE_DATA)
    130             byte b;
    131             charloop: while (length-->0)
    132             {
    133                 switch (_state)
    134                 {
    135                     case STATE_START:
    136                         b=_buffer.get();
    137                         _opcode=b;
    138                         if (_opcode<0)
    139                         {
    140                             _length=0;
    141                             _state=STATE_LENGTH;
    142                         }
    143                         else
    144                         {
    145                             _state=STATE_SENTINEL_DATA;
    146                             _buffer.mark(0);
    147                         }
    148                         continue;
    149 
    150                     case STATE_SENTINEL_DATA:
    151                         b=_buffer.get();
    152                         if ((b&0xff)==0xff)
    153                         {
    154                             _state=STATE_START;
    155                             int l=_buffer.getIndex()-_buffer.markIndex()-1;
    156                             progress++;
    157                             _handler.onFrame((byte)0,_opcode,_buffer.sliceFromMark(l));
    158                             _buffer.setMarkIndex(-1);
    159                             if (_buffer.length()==0)
    160                             {
    161                                 _buffers.returnBuffer(_buffer);
    162                                 _buffer=null;
    163                             }
    164                             return progress;
    165                         }
    166                         continue;
    167 
    168                     case STATE_LENGTH:
    169                         b=_buffer.get();
    170                         _length=_length<<7 | (0x7f&b);
    171                         if (b>=0)
    172                         {
    173                             _state=STATE_DATA;
    174                             _buffer.mark(0);
    175                         }
    176                         continue;
    177 
    178                     case STATE_DATA:
    179                         if (_buffer.markIndex()<0)
    180                         if (_buffer.length()<_length)
    181                             break charloop;
    182                         Buffer data=_buffer.sliceFromMark(_length);
    183                         _buffer.skip(_length);
    184                         _state=STATE_START;
    185                         progress++;
    186                         _handler.onFrame((byte)0, _opcode, data);
    187 
    188                         if (_buffer.length()==0)
    189                         {
    190                             _buffers.returnBuffer(_buffer);
    191                             _buffer=null;
    192                         }
    193 
    194                         return progress;
    195                 }
    196             }
    197         }
    198     }
    199 
    200     /* ------------------------------------------------------------ */
    201     public void fill(Buffer buffer)
    202     {
    203         if (buffer!=null && buffer.length()>0)
    204         {
    205             if (_buffer==null)
    206                 _buffer=_buffers.getBuffer();
    207             _buffer.put(buffer);
    208             buffer.clear();
    209         }
    210     }
    211 
    212 }
    213