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.TypeUtil;
22  import org.eclipse.jetty.util.Utf8StringBuilder;
23  import org.eclipse.jetty.util.log.Log;
24  
25  
26  
27  /* ------------------------------------------------------------ */
28  /**
29   * Parser the WebSocket protocol.
30   *
31   */
32  public class WebSocketParserD07 implements WebSocketParser
33  {    
34      public enum State { 
35          
36          START(0), OPCODE(1), LENGTH_7(1), LENGTH_16(2), LENGTH_63(8), MASK(4), PAYLOAD(0), DATA(0), SKIP(1);
37  
38          int _needs;
39  
40          State(int needs)
41          {
42              _needs=needs;
43          }
44  
45          int getNeeds()
46          {
47              return _needs;
48          }
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  
67      /* ------------------------------------------------------------ */
68      /**
69       * @param buffers The buffers to use for parsing.  Only the {@link Buffers#getBuffer()} is used.
70       * This should be a direct buffer if binary data is mostly used or an indirect buffer if utf-8 data
71       * is mostly used.
72       * @param endp
73       * @param handler
74       */
75      public WebSocketParserD07(WebSocketBuffers buffers, EndPoint endp, FrameHandler handler, boolean shouldBeMasked)
76      {
77          _buffers=buffers;
78          _endp=endp;
79          _handler=handler;
80          _shouldBeMasked=shouldBeMasked;
81          _state=State.START;
82      }
83  
84      /* ------------------------------------------------------------ */
85      public boolean isBufferEmpty()
86      {
87          return _buffer==null || _buffer.length()==0;
88      }
89  
90      /* ------------------------------------------------------------ */
91      public Buffer getBuffer()
92      {
93          return _buffer;
94      }
95  
96      /* ------------------------------------------------------------ */
97      /** Parse to next event.
98       * Parse to the next {@link WebSocketParser.FrameHandler} event or until no more data is
99       * available. Fill data from the {@link EndPoint} only as necessary.
100      * @return An indication of progress or otherwise. -1 indicates EOF, 0 indicates
101      * that no bytes were read and no messages parsed. A positive number indicates either
102      * the bytes filled or the messages parsed.
103      */
104     public int parseNext()
105     {
106         if (_buffer==null)
107             _buffer=_buffers.getBuffer();
108 
109         int total_filled=0;
110         int events=0;
111 
112         // Loop until a datagram call back or can't fill anymore
113         while(true)
114         {
115             int available=_buffer.length();
116 
117             // Fill buffer if we need a byte or need length
118             while (available<(_state==State.SKIP?1:_bytesNeeded))
119             {
120                 // compact to mark (set at start of data)
121                 _buffer.compact();
122 
123                 // if no space, then the data is too big for buffer
124                 if (_buffer.space() == 0)
125                     throw new IllegalStateException("FULL: "+_state+" "+_bytesNeeded+">"+_buffer.capacity());
126 
127                 // catch IOExceptions (probably EOF) and try to parse what we have
128                 try
129                 {
130                     int filled=_endp.isOpen()?_endp.fill(_buffer):-1;
131                     if (filled<=0)
132                         return (total_filled+events)>0?(total_filled+events):filled;
133                     total_filled+=filled;
134                     available=_buffer.length();
135                 }
136                 catch(IOException e)
137                 {
138                     Log.debug(e);
139                     return (total_filled+events)>0?(total_filled+events):-1;
140                 }
141             }
142 
143             // if we are here, then we have sufficient bytes to process the current state.
144   
145             // Parse the buffer byte by byte (unless it is STATE_DATA)
146             byte b;
147             while (_state!=State.DATA && available>=(_state==State.SKIP?1:_bytesNeeded))
148             {
149                 switch (_state)
150                 {
151                     case START:
152                         _skip=false;
153                         _state=State.OPCODE;
154                         _bytesNeeded=_state.getNeeds();
155                         continue;
156                         
157                     case OPCODE:
158                         b=_buffer.get();
159                         available--;
160                         _opcode=(byte)(b&0xf);
161                         _flags=(byte)(0xf&(b>>4));
162                         
163                         if (WebSocketConnectionD07.isControlFrame(_opcode)&&!WebSocketConnectionD07.isLastFrame(_flags))
164                         {
165                             events++;
166                             Log.warn("Fragmented Control from "+_endp);
167                             _handler.close(WebSocketConnectionD07.CLOSE_PROTOCOL,"Fragmented control");
168                             _skip=true;
169                         }
170 
171                         _state=State.LENGTH_7;
172                         _bytesNeeded=_state.getNeeds();
173 
174                         continue;
175 
176                     case LENGTH_7:
177                         b=_buffer.get();
178                         available--;
179                         _masked=(b&0x80)!=0;
180                         b=(byte)(0x7f&b);
181                         
182                         switch(b)
183                         {
184                             case 0x7f:
185                                 _length=0;
186                                 _state=State.LENGTH_63;
187                                 break;
188                             case 0x7e:
189                                 _length=0;
190                                 _state=State.LENGTH_16;
191                                 break;
192                             default:
193                                 _length=(0x7f&b);
194                                 _state=_masked?State.MASK:State.PAYLOAD; 
195                         }
196                         _bytesNeeded=_state.getNeeds();
197                         continue;
198 
199                     case LENGTH_16:
200                         b=_buffer.get();
201                         available--;
202                         _length = _length*0x100 + (0xff&b);
203                         if (--_bytesNeeded==0)
204                         {
205                             if (_length>_buffer.capacity())
206                             {
207                                 events++;
208                                 _handler.close(WebSocketConnectionD07.CLOSE_LARGE,"frame size "+_length+">"+_buffer.capacity());
209                                 _skip=true;
210                             }
211 
212                             _state=_masked?State.MASK:State.PAYLOAD; 
213                             _bytesNeeded=_state.getNeeds();
214                         }
215                         continue;
216 
217                     case LENGTH_63:
218                         b=_buffer.get();
219                         available--;
220                         _length = _length*0x100 + (0xff&b);
221                         if (--_bytesNeeded==0)
222                         {
223                             _bytesNeeded=(int)_length;
224                             if (_length>=_buffer.capacity())
225                             {
226                                 events++;
227                                 _handler.close(WebSocketConnectionD07.CLOSE_LARGE,"frame size "+_length+">"+_buffer.capacity());
228                                 _skip=true;
229                             }
230 
231                             _state=_masked?State.MASK:State.PAYLOAD; 
232                             _bytesNeeded=_state.getNeeds();
233                         }
234                         continue;
235 
236                     case MASK:
237                         _buffer.get(_mask,0,4);
238                         _m=0;
239                         available-=4;
240                         _state=State.PAYLOAD;
241                         _bytesNeeded=_state.getNeeds();
242                         break;
243 
244                     case PAYLOAD:
245                         _bytesNeeded=(int)_length;
246                         _state=_skip?State.SKIP:State.DATA;
247                         break;
248                         
249                     case DATA:
250                         break;
251                         
252                     case SKIP:
253                         int skip=Math.min(available,_bytesNeeded);
254                         _buffer.skip(skip);
255                         available-=skip;
256                         _bytesNeeded-=skip;
257                         if (_bytesNeeded==0)
258                             _state=State.START;
259                         
260                 }
261             }
262 
263             if (_state==State.DATA && available>=_bytesNeeded)
264             {
265                 if ( _masked!=_shouldBeMasked)
266                 {
267                     _buffer.skip(_bytesNeeded);
268                     _state=State.START;
269                     events++;
270                     _handler.close(WebSocketConnectionD07.CLOSE_PROTOCOL,"bad mask");
271                 }
272                 else
273                 {
274                     Buffer data =_buffer.get(_bytesNeeded);
275                     if (_masked)
276                     {
277                         if (data.array()==null)
278                             data=_buffer.asMutableBuffer();
279                         byte[] array = data.array();
280                         final int end=data.putIndex();
281                         for (int i=data.getIndex();i<end;i++)
282                             array[i]^=_mask[_m++%4];
283                     }
284 
285                     // System.err.printf("%s %s %s >>\n",TypeUtil.toHexString(_flags),TypeUtil.toHexString(_opcode),data.length());
286                     events++;
287                     _handler.onFrame(_flags, _opcode, data);
288                     _bytesNeeded=0;
289                     _state=State.START;
290                 }
291 
292                 if (_buffer.length()==0)
293                 {
294                     _buffers.returnBuffer(_buffer);
295                     _buffer=null;
296                 }
297 
298                 return total_filled+events;
299             }
300         }
301     }
302 
303     /* ------------------------------------------------------------ */
304     public void fill(Buffer buffer)
305     {
306         if (buffer!=null && buffer.length()>0)
307         {
308             if (_buffer==null)
309                 _buffer=_buffers.getBuffer();
310             _buffer.put(buffer);
311             buffer.clear();
312         }
313     }
314 
315 }