View Javadoc

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