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