View Javadoc

1   //
2   //  ========================================================================
3   //  Copyright (c) 1995-2013 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 WebSocketParserRFC6455 implements WebSocketParser
37  {
38      private static final Logger LOG = Log.getLogger(WebSocketParserRFC6455.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 WebSocketParserRFC6455(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^WebSocketConnectionRFC6455.FLAG_FIN)), _opcode, data);
170 
171                         _opcode=WebSocketConnectionRFC6455.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==WebSocketConnectionRFC6455.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 (WebSocketConnectionRFC6455.isControlFrame(_opcode)&&!WebSocketConnectionRFC6455.isLastFrame(_flags))
218                         {
219                             LOG.warn("Fragmented Control from "+_endp);
220                             _handler.close(WebSocketConnectionRFC6455.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(WebSocketConnectionRFC6455.CLOSE_POLICY_VIOLATION,"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(WebSocketConnectionRFC6455.CLOSE_POLICY_VIOLATION,"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(WebSocketConnectionRFC6455.CLOSE_PROTOCOL,"Not masked");
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 }