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 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);
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 _fakeFragments=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 _fakeFragments;
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         _fakeFragments = 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         int total_filled=0;
134         int events=0;
135 
136         // Loop until a datagram call back or can't fill anymore
137         while(true)
138         {
139             int available=_buffer.length();
140 
141             // Fill buffer if we need a byte or need length
142             while (available<(_state==State.SKIP?1:_bytesNeeded))
143             {
144                 // compact to mark (set at start of data)
145                 _buffer.compact();
146 
147                 // if no space, then the data is too big for buffer
148                 if (_buffer.space() == 0)
149                 {
150                     // Can we send a fake frame?
151                     if (_fakeFragments && _state==State.DATA)
152                     {
153                         Buffer data =_buffer.get(4*(available/4));
154                         _buffer.compact();
155                         if (_masked)
156                         {
157                             if (data.array()==null)
158                                 data=_buffer.asMutableBuffer();
159                             byte[] array = data.array();
160                             final int end=data.putIndex();
161                             for (int i=data.getIndex();i<end;i++)
162                                 array[i]^=_mask[_m++%4];
163                         }
164 
165                         // System.err.printf("%s %s %s >>\n",TypeUtil.toHexString(_flags),TypeUtil.toHexString(_opcode),data.length());
166                         events++;
167                         _bytesNeeded-=data.length();
168                         _handler.onFrame((byte)(_flags&(0xff^WebSocketConnectionD08.FLAG_FIN)), _opcode, data);
169 
170                         _opcode=WebSocketConnectionD08.OP_CONTINUATION;
171                     }
172 
173                     if (_buffer.space() == 0)
174                     throw new IllegalStateException("FULL: "+_state+" "+_bytesNeeded+">"+_buffer.capacity());
175                 }
176 
177                 // catch IOExceptions (probably EOF) and try to parse what we have
178                 try
179                 {
180                     int filled=_endp.isOpen()?_endp.fill(_buffer):-1;
181                     if (filled<=0)
182                         return (total_filled+events)>0?(total_filled+events):filled;
183                     total_filled+=filled;
184                     available=_buffer.length();
185                 }
186                 catch(IOException e)
187                 {
188                     LOG.debug(e);
189                     return (total_filled+events)>0?(total_filled+events):-1;
190                 }
191             }
192 
193             // if we are here, then we have sufficient bytes to process the current state.
194 
195             // Parse the buffer byte by byte (unless it is STATE_DATA)
196             byte b;
197             while (_state!=State.DATA && available>=(_state==State.SKIP?1:_bytesNeeded))
198             {
199                 switch (_state)
200                 {
201                     case START:
202                         _skip=false;
203                         _state=State.OPCODE;
204                         _bytesNeeded=_state.getNeeds();
205                         continue;
206 
207                     case OPCODE:
208                         b=_buffer.get();
209                         available--;
210                         _opcode=(byte)(b&0xf);
211                         _flags=(byte)(0xf&(b>>4));
212 
213                         if (WebSocketConnectionD08.isControlFrame(_opcode)&&!WebSocketConnectionD08.isLastFrame(_flags))
214                         {
215                             events++;
216                             LOG.warn("Fragmented Control from "+_endp);
217                             _handler.close(WebSocketConnectionD08.CLOSE_PROTOCOL,"Fragmented control");
218                             _skip=true;
219                         }
220 
221                         _state=State.LENGTH_7;
222                         _bytesNeeded=_state.getNeeds();
223 
224                         continue;
225 
226                     case LENGTH_7:
227                         b=_buffer.get();
228                         available--;
229                         _masked=(b&0x80)!=0;
230                         b=(byte)(0x7f&b);
231 
232                         switch(b)
233                         {
234                             case 0x7f:
235                                 _length=0;
236                                 _state=State.LENGTH_63;
237                                 break;
238                             case 0x7e:
239                                 _length=0;
240                                 _state=State.LENGTH_16;
241                                 break;
242                             default:
243                                 _length=(0x7f&b);
244                                 _state=_masked?State.MASK:State.PAYLOAD;
245                         }
246                         _bytesNeeded=_state.getNeeds();
247                         continue;
248 
249                     case LENGTH_16:
250                         b=_buffer.get();
251                         available--;
252                         _length = _length*0x100 + (0xff&b);
253                         if (--_bytesNeeded==0)
254                         {
255                             if (_length>_buffer.capacity() && !_fakeFragments)
256                             {
257                                 events++;
258                                 _handler.close(WebSocketConnectionD08.CLOSE_BADDATA,"frame size "+_length+">"+_buffer.capacity());
259                                 _skip=true;
260                             }
261 
262                             _state=_masked?State.MASK:State.PAYLOAD;
263                             _bytesNeeded=_state.getNeeds();
264                         }
265                         continue;
266 
267                     case LENGTH_63:
268                         b=_buffer.get();
269                         available--;
270                         _length = _length*0x100 + (0xff&b);
271                         if (--_bytesNeeded==0)
272                         {
273                             _bytesNeeded=(int)_length;
274                             if (_length>=_buffer.capacity() && !_fakeFragments)
275                             {
276                                 events++;
277                                 _handler.close(WebSocketConnectionD08.CLOSE_BADDATA,"frame size "+_length+">"+_buffer.capacity());
278                                 _skip=true;
279                             }
280 
281                             _state=_masked?State.MASK:State.PAYLOAD;
282                             _bytesNeeded=_state.getNeeds();
283                         }
284                         continue;
285 
286                     case MASK:
287                         _buffer.get(_mask,0,4);
288                         _m=0;
289                         available-=4;
290                         _state=State.PAYLOAD;
291                         _bytesNeeded=_state.getNeeds();
292                         break;
293 
294                     case PAYLOAD:
295                         _bytesNeeded=(int)_length;
296                         _state=_skip?State.SKIP:State.DATA;
297                         break;
298 
299                     case DATA:
300                         break;
301 
302                     case SKIP:
303                         int skip=Math.min(available,_bytesNeeded);
304                         _buffer.skip(skip);
305                         available-=skip;
306                         _bytesNeeded-=skip;
307                         if (_bytesNeeded==0)
308                             _state=State.START;
309 
310                 }
311             }
312 
313             if (_state==State.DATA && available>=_bytesNeeded)
314             {
315                 if ( _masked!=_shouldBeMasked)
316                 {
317                     _buffer.skip(_bytesNeeded);
318                     _state=State.START;
319                     events++;
320                     _handler.close(WebSocketConnectionD08.CLOSE_PROTOCOL,"bad mask");
321                 }
322                 else
323                 {
324                     Buffer data =_buffer.get(_bytesNeeded);
325                     if (_masked)
326                     {
327                         if (data.array()==null)
328                             data=_buffer.asMutableBuffer();
329                         byte[] array = data.array();
330                         final int end=data.putIndex();
331                         for (int i=data.getIndex();i<end;i++)
332                             array[i]^=_mask[_m++%4];
333                     }
334 
335                     // System.err.printf("%s %s %s >>\n",TypeUtil.toHexString(_flags),TypeUtil.toHexString(_opcode),data.length());
336                     events++;
337                     _handler.onFrame(_flags, _opcode, data);
338                     _bytesNeeded=0;
339                     _state=State.START;
340                 }
341 
342                 return total_filled+events;
343             }
344         }
345     }
346 
347     /* ------------------------------------------------------------ */
348     public void fill(Buffer buffer)
349     {
350         if (buffer!=null && buffer.length()>0)
351         {
352             if (_buffer==null)
353                 _buffer=_buffers.getBuffer();
354 
355             _buffer.put(buffer);
356             buffer.clear();
357         }
358     }
359 
360     /* ------------------------------------------------------------ */
361     public void returnBuffer()
362     {
363         if (_buffer!=null && _buffer.length()==0)
364         {
365             _buffers.returnBuffer(_buffer);
366             _buffer=null;
367         }
368     }
369 
370     /* ------------------------------------------------------------ */
371     @Override
372     public String toString()
373     {
374         Buffer buffer=_buffer;
375         return WebSocketParserD08.class.getSimpleName()+"@"+ Integer.toHexString(hashCode())+"|"+_state+"|"+(buffer==null?"<>":buffer.toDetailString());
376     }
377 
378 }