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 WebSocketParserD06 implements WebSocketParser
33  {    
34      public enum State { 
35          MASK(0), OPCODE(1), LENGTH_7(2), LENGTH_16(4), LENGTH_63(10), DATA(10);
36  
37          int _minSize;
38  
39          State(int minSize)
40          {
41              _minSize=minSize;
42          }
43  
44          int getMinSize()
45          {
46              return _minSize;
47          }
48      };
49  
50  
51      private final WebSocketBuffers _buffers;
52      private final EndPoint _endp;
53      private final FrameHandler _handler;
54      private final boolean _masked;
55      private State _state;
56      private Buffer _buffer;
57      private boolean _fin;
58      private byte _flags;
59      private byte _opcode;
60      private int _count;
61      private long _length;
62      private Utf8StringBuilder _utf8;
63      private final byte[] _mask = new byte[4];
64      private int _m;
65  
66      /* ------------------------------------------------------------ */
67      /**
68       * @param buffers The buffers to use for parsing.  Only the {@link Buffers#getBuffer()} is used.
69       * This should be a direct buffer if binary data is mostly used or an indirect buffer if utf-8 data
70       * is mostly used.
71       * @param endp
72       * @param handler
73       */
74      public WebSocketParserD06(WebSocketBuffers buffers, EndPoint endp, FrameHandler handler, boolean masked)
75      {
76          _buffers=buffers;
77          _endp=endp;
78          _handler=handler;
79          _masked=masked;
80          _state=_masked?State.MASK:State.OPCODE;
81      }
82  
83      /* ------------------------------------------------------------ */
84      public boolean isBufferEmpty()
85      {
86          return _buffer==null || _buffer.length()==0;
87      }
88  
89      /* ------------------------------------------------------------ */
90      public Buffer getBuffer()
91      {
92          return _buffer;
93      }
94  
95      /* ------------------------------------------------------------ */
96      /** Parse to next event.
97       * Parse to the next {@link WebSocketParser.FrameHandler} event or until no more data is
98       * available. Fill data from the {@link EndPoint} only as necessary.
99       * @return An indication of progress or otherwise. -1 indicates EOF, 0 indicates
100      * that no bytes were read and no messages parsed. A positive number indicates either
101      * the bytes filled or the messages parsed.
102      */
103     public int parseNext()
104     {
105         if (_buffer==null)
106             _buffer=_buffers.getBuffer();
107 
108         int total_filled=0;
109 
110         // Loop until an datagram call back or can't fill anymore
111         while(true)
112         {
113             int available=_buffer.length();
114 
115             // Fill buffer if we need a byte or need length
116             if (available < (_state.getMinSize() + (_masked?4:0)) || _state==State.DATA && available<_count)
117             {
118                 // compact to mark (set at start of data)
119                 _buffer.compact();
120 
121                 // if no space, then the data is too big for buffer
122                 if (_buffer.space() == 0)
123                     throw new IllegalStateException("FULL");
124 
125                 // catch IOExceptions (probably EOF) and try to parse what we have
126                 try
127                 {
128                     int filled=_endp.isOpen()?_endp.fill(_buffer):-1;
129                     if (filled<=0)
130                         return total_filled;
131                     total_filled+=filled;
132                     available=_buffer.length();
133                 }
134                 catch(IOException e)
135                 {
136                     Log.debug(e);
137                     return total_filled>0?total_filled:-1;
138                 }
139             }
140 
141             // if we are here, then we have sufficient bytes to process the current state.
142             
143             // Parse the buffer byte by byte (unless it is STATE_DATA)
144             byte b;
145             while (_state!=State.DATA && available-->0)
146             {
147                 switch (_state)
148                 {
149                     case MASK:
150                         _buffer.get(_mask,0,4);
151                         _state=State.OPCODE;
152                         _m=0;
153                         continue;
154                         
155                     case OPCODE:
156                         b=_buffer.get();
157                         if (_masked)
158                             b^=_mask[_m++%4];
159                         _opcode=(byte)(b&0xf);
160                         _flags=(byte)(b>>4);
161                         _fin=(_flags&8)!=0;
162                         _state=State.LENGTH_7;
163                         continue;
164 
165                     case LENGTH_7:
166                         b=_buffer.get();
167                         if (_masked)
168                             b^=_mask[_m++%4];
169                         switch(b)
170                         {
171                             case 127:
172                                 _length=0;
173                                 _count=8;
174                                 _state=State.LENGTH_63;
175                                 break;
176                             case 126:
177                                 _length=0;
178                                 _count=2;
179                                 _state=State.LENGTH_16;
180                                 break;
181                             default:
182                                 _length=(0x7f&b);
183                                 _count=(int)_length;
184                                 _state=State.DATA; 
185                         }
186                         continue;
187 
188                     case LENGTH_16:
189                         b=_buffer.get();
190                         if (_masked)
191                             b^=_mask[_m++%4];
192                         _length = _length<<8 | b;
193                         if (--_count==0)
194                         {
195                             if (_length>=_buffer.capacity()-4)
196                                 throw new IllegalStateException("TOO LARGE");
197                             _count=(int)_length;
198                             _state=State.DATA;
199                         }
200                         continue;
201 
202                     case LENGTH_63:
203                         b=_buffer.get();
204                         if (_masked)
205                             b^=_mask[_m++%4];
206                         _length = _length<<8 | b;
207                         if (--_count==0)
208                         {
209                             if (_length>=_buffer.capacity()-10)
210                                 throw new IllegalStateException("TOO LARGE");
211                             _count=(int)_length;
212                             _state=State.DATA;
213                         }
214                         continue;
215                 }
216             }
217 
218             if (_state==State.DATA && available>=_count)
219             {
220                 Buffer data =_buffer.get(_count);
221                 if (_masked)
222                 {
223                     if (data.array()==null)
224                         data=_buffer.asMutableBuffer();
225                     byte[] array = data.array();
226                     final int end=data.putIndex();
227                     for (int i=data.getIndex();i<end;i++)
228                         array[i]^=_mask[_m++%4];
229                 }
230                 
231                 _handler.onFrame(!_fin,_flags, _opcode, data);
232                 _count=0;
233                 _state=_masked?State.MASK:State.OPCODE;
234 
235                 if (_buffer.length()==0)
236                 {
237                     _buffers.returnBuffer(_buffer);
238                     _buffer=null;
239                 }
240 
241                 return total_filled;
242 
243             }
244         }
245     }
246 
247     /* ------------------------------------------------------------ */
248     public void fill(Buffer buffer)
249     {
250         if (buffer!=null && buffer.length()>0)
251         {
252             if (_buffer==null)
253                 _buffer=_buffers.getBuffer();
254             _buffer.put(buffer);
255             buffer.clear();
256         }
257     }
258 
259 }