View Javadoc

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