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.log.Log;
     27 import org.eclipse.jetty.util.log.Logger;
     28 
     29 
     30 
     31 /* ------------------------------------------------------------ */
     32 /**
     33  * Parser the WebSocket protocol.
     34  *
     35  */
     36 public class WebSocketParserD08 implements WebSocketParser
     37 {
     38     private static final Logger LOG = Log.getLogger(WebSocketParserD08.class);
     39 
     40     public enum State {
     41 
     42         START(0), OPCODE(1), LENGTH_7(1), LENGTH_16(2), LENGTH_63(8), MASK(4), PAYLOAD(0), DATA(0), SKIP(1), SEEK_EOF(1);
     43 
     44         int _needs;
     45 
     46         State(int needs)
     47         {
     48             _needs=needs;
     49         }
     50 
     51         int getNeeds()
     52         {
     53             return _needs;
     54         }
     55     }
     56 
     57     private final WebSocketBuffers _buffers;
     58     private final EndPoint _endp;
     59     private final FrameHandler _handler;
     60     private final boolean _shouldBeMasked;
     61     private State _state;
     62     private Buffer _buffer;
     63     private byte _flags;
     64     private byte _opcode;
     65     private int _bytesNeeded;
     66     private long _length;
     67     private boolean _masked;
     68     private final byte[] _mask = new byte[4];
     69     private int _m;
     70     private boolean _skip;
     71     private boolean _fragmentFrames=true;
     72 
     73     /* ------------------------------------------------------------ */
     74     /**
     75      * @param buffers The buffers to use for parsing.  Only the {@link Buffers#getBuffer()} is used.
     76      * This should be a direct buffer if binary data is mostly used or an indirect buffer if utf-8 data
     77      * is mostly used.
     78      * @param endp the endpoint
     79      * @param handler the handler to notify when a parse event occurs
     80      * @param shouldBeMasked whether masking should be handled
     81      */
     82     public WebSocketParserD08(WebSocketBuffers buffers, EndPoint endp, FrameHandler handler, boolean shouldBeMasked)
     83     {
     84         _buffers=buffers;
     85         _endp=endp;
     86         _handler=handler;
     87         _shouldBeMasked=shouldBeMasked;
     88         _state=State.START;
     89     }
     90 
     91     /* ------------------------------------------------------------ */
     92     /**
     93      * @return True if fake fragments should be created for frames larger than the buffer.
     94      */
     95     public boolean isFakeFragments()
     96     {
     97         return _fragmentFrames;
     98     }
     99 
    100     /* ------------------------------------------------------------ */
    101     /**
    102      * @param fakeFragments True if fake fragments should be created for frames larger than the buffer.
    103      */
    104     public void setFakeFragments(boolean fakeFragments)
    105     {
    106         _fragmentFrames = fakeFragments;
    107     }
    108 
    109     /* ------------------------------------------------------------ */
    110     public boolean isBufferEmpty()
    111     {
    112         return _buffer==null || _buffer.length()==0;
    113     }
    114 
    115     /* ------------------------------------------------------------ */
    116     public Buffer getBuffer()
    117     {
    118         return _buffer;
    119     }
    120 
    121     /* ------------------------------------------------------------ */
    122     /** Parse to next event.
    123      * Parse to the next {@link WebSocketParser.FrameHandler} event or until no more data is
    124      * available. Fill data from the {@link EndPoint} only as necessary.
    125      * @return An indication of progress or otherwise. -1 indicates EOF, 0 indicates
    126      * that no bytes were read and no messages parsed. A positive number indicates either
    127      * the bytes filled or the messages parsed.
    128      */
    129     public int parseNext()
    130     {
    131         if (_buffer==null)
    132             _buffer=_buffers.getBuffer();
    133 
    134         boolean progress=false;
    135         int filled=-1;
    136 
    137         // Loop until a datagram call back or can't fill anymore
    138         while(!progress && (!_endp.isInputShutdown()||_buffer.length()>0))
    139         {
    140             int available=_buffer.length();
    141 
    142             // Fill buffer if we need a byte or need length
    143             while (available<(_state==State.SKIP?1:_bytesNeeded))
    144             {
    145                 // compact to mark (set at start of data)
    146                 _buffer.compact();
    147 
    148                 // if no space, then the data is too big for buffer
    149                 if (_buffer.space() == 0)
    150                 {
    151                     // Can we send a fake frame?
    152                     if (_fragmentFrames && _state==State.DATA)
    153                     {
    154                         Buffer data =_buffer.get(4*(available/4));
    155                         _buffer.compact();
    156                         if (_masked)
    157                         {
    158                             if (data.array()==null)
    159                                 data=_buffer.asMutableBuffer();
    160                             byte[] array = data.array();
    161                             final int end=data.putIndex();
    162                             for (int i=data.getIndex();i<end;i++)
    163                                 array[i]^=_mask[_m++%4];
    164                         }
    165 
    166                         // System.err.printf("%s %s %s >>\n",TypeUtil.toHexString(_flags),TypeUtil.toHexString(_opcode),data.length());
    167                         _bytesNeeded-=data.length();
    168                         progress=true;
    169                         _handler.onFrame((byte)(_flags&(0xff^WebSocketConnectionD08.FLAG_FIN)), _opcode, data);
    170 
    171                         _opcode=WebSocketConnectionD08.OP_CONTINUATION;
    172                     }
    173 
    174                     if (_buffer.space() == 0)
    175                         throw new IllegalStateException("FULL: "+_state+" "+_bytesNeeded+">"+_buffer.capacity());
    176                 }
    177 
    178                 // catch IOExceptions (probably EOF) and try to parse what we have
    179                 try
    180                 {
    181                     filled=_endp.isInputShutdown()?-1:_endp.fill(_buffer);
    182                     available=_buffer.length();
    183                     // System.err.printf(">> filled %d/%d%n",filled,available);
    184                     if (filled<=0)
    185                         break;
    186                 }
    187                 catch(IOException e)
    188                 {
    189                     LOG.debug(e);
    190                     filled=-1;
    191                     break;
    192                 }
    193             }
    194             // Did we get enough?
    195             if (available<(_state==State.SKIP?1:_bytesNeeded))
    196                 break;
    197 
    198             // if we are here, then we have sufficient bytes to process the current state.
    199             // Parse the buffer byte by byte (unless it is STATE_DATA)
    200             byte b;
    201             while (_state!=State.DATA && available>=(_state==State.SKIP?1:_bytesNeeded))
    202             {
    203                 switch (_state)
    204                 {
    205                     case START:
    206                         _skip=false;
    207                         _state=_opcode==WebSocketConnectionD08.OP_CLOSE?State.SEEK_EOF:State.OPCODE;
    208                         _bytesNeeded=_state.getNeeds();
    209                         continue;
    210 
    211                     case OPCODE:
    212                         b=_buffer.get();
    213                         available--;
    214                         _opcode=(byte)(b&0xf);
    215                         _flags=(byte)(0xf&(b>>4));
    216 
    217                         if (WebSocketConnectionD08.isControlFrame(_opcode)&&!WebSocketConnectionD08.isLastFrame(_flags))
    218                         {
    219                             LOG.warn("Fragmented Control from "+_endp);
    220                             _handler.close(WebSocketConnectionD08.CLOSE_PROTOCOL,"Fragmented control");
    221                             progress=true;
    222                             _skip=true;
    223                         }
    224 
    225                         _state=State.LENGTH_7;
    226                         _bytesNeeded=_state.getNeeds();
    227 
    228                         continue;
    229 
    230                     case LENGTH_7:
    231                         b=_buffer.get();
    232                         available--;
    233                         _masked=(b&0x80)!=0;
    234                         b=(byte)(0x7f&b);
    235 
    236                         switch(b)
    237                         {
    238                             case 0x7f:
    239                                 _length=0;
    240                                 _state=State.LENGTH_63;
    241                                 break;
    242                             case 0x7e:
    243                                 _length=0;
    244                                 _state=State.LENGTH_16;
    245                                 break;
    246                             default:
    247                                 _length=(0x7f&b);
    248                                 _state=_masked?State.MASK:State.PAYLOAD;
    249                         }
    250                         _bytesNeeded=_state.getNeeds();
    251                         continue;
    252 
    253                     case LENGTH_16:
    254                         b=_buffer.get();
    255                         available--;
    256                         _length = _length*0x100 + (0xff&b);
    257                         if (--_bytesNeeded==0)
    258                         {
    259                             if (_length>_buffer.capacity() && !_fragmentFrames)
    260                             {
    261                                 progress=true;
    262                                 _handler.close(WebSocketConnectionD08.CLOSE_BADDATA,"frame size "+_length+">"+_buffer.capacity());
    263                                 _skip=true;
    264                             }
    265 
    266                             _state=_masked?State.MASK:State.PAYLOAD;
    267                             _bytesNeeded=_state.getNeeds();
    268                         }
    269                         continue;
    270 
    271                     case LENGTH_63:
    272                         b=_buffer.get();
    273                         available--;
    274                         _length = _length*0x100 + (0xff&b);
    275                         if (--_bytesNeeded==0)
    276                         {
    277                             _bytesNeeded=(int)_length;
    278                             if (_length>=_buffer.capacity() && !_fragmentFrames)
    279                             {
    280                                 progress=true;
    281                                 _handler.close(WebSocketConnectionD08.CLOSE_BADDATA,"frame size "+_length+">"+_buffer.capacity());
    282                                 _skip=true;
    283                             }
    284 
    285                             _state=_masked?State.MASK:State.PAYLOAD;
    286                             _bytesNeeded=_state.getNeeds();
    287                         }
    288                         continue;
    289 
    290                     case MASK:
    291                         _buffer.get(_mask,0,4);
    292                         _m=0;
    293                         available-=4;
    294                         _state=State.PAYLOAD;
    295                         _bytesNeeded=_state.getNeeds();
    296                         break;
    297 
    298                     case PAYLOAD:
    299                         _bytesNeeded=(int)_length;
    300                         _state=_skip?State.SKIP:State.DATA;
    301                         break;
    302 
    303                     case DATA:
    304                         break;
    305 
    306                     case SKIP:
    307                         int skip=Math.min(available,_bytesNeeded);
    308                         progress=true;
    309                         _buffer.skip(skip);
    310                         available-=skip;
    311                         _bytesNeeded-=skip;
    312                         if (_bytesNeeded==0)
    313                             _state=State.START;
    314                         break;
    315 
    316                     case SEEK_EOF:
    317                         progress=true;
    318                         _buffer.skip(available);
    319                         available=0;
    320                         break;
    321                 }
    322             }
    323 
    324             if (_state==State.DATA && available>=_bytesNeeded)
    325             {
    326                 if ( _masked!=_shouldBeMasked)
    327                 {
    328                     _buffer.skip(_bytesNeeded);
    329                     _state=State.START;
    330                     progress=true;
    331                     _handler.close(WebSocketConnectionD08.CLOSE_PROTOCOL,"bad mask");
    332                 }
    333                 else
    334                 {
    335                     Buffer data =_buffer.get(_bytesNeeded);
    336                     if (_masked)
    337                     {
    338                         if (data.array()==null)
    339                             data=_buffer.asMutableBuffer();
    340                         byte[] array = data.array();
    341                         final int end=data.putIndex();
    342                         for (int i=data.getIndex();i<end;i++)
    343                             array[i]^=_mask[_m++%4];
    344                     }
    345 
    346                     // System.err.printf("%s %s %s >>\n",TypeUtil.toHexString(_flags),TypeUtil.toHexString(_opcode),data.length());
    347 
    348                     progress=true;
    349                     _handler.onFrame(_flags, _opcode, data);
    350                     _bytesNeeded=0;
    351                     _state=State.START;
    352                 }
    353 
    354                 break;
    355             }
    356         }
    357 
    358         return progress?1:filled;
    359     }
    360 
    361     /* ------------------------------------------------------------ */
    362     public void fill(Buffer buffer)
    363     {
    364         if (buffer!=null && buffer.length()>0)
    365         {
    366             if (_buffer==null)
    367                 _buffer=_buffers.getBuffer();
    368 
    369             _buffer.put(buffer);
    370             buffer.clear();
    371         }
    372     }
    373 
    374     /* ------------------------------------------------------------ */
    375     public void returnBuffer()
    376     {
    377         if (_buffer!=null && _buffer.length()==0)
    378         {
    379             _buffers.returnBuffer(_buffer);
    380             _buffer=null;
    381         }
    382     }
    383 
    384     /* ------------------------------------------------------------ */
    385     @Override
    386     public String toString()
    387     {
    388         return String.format("%s@%x state=%s buffer=%s",
    389                 getClass().getSimpleName(),
    390                 hashCode(),
    391                 _state,
    392                 _buffer);
    393     }
    394 }
    395