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 }