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