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