blob: 362414a8d989669b1e59807aa2a60e82bcc38d4c [file] [log] [blame]
Jake Slack03928ae2014-05-13 18:41:56 -07001//
2// ========================================================================
3// Copyright (c) 1995-2014 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
19package org.eclipse.jetty.websocket;
20
21import java.io.IOException;
22
23import org.eclipse.jetty.io.Buffer;
24import org.eclipse.jetty.io.Buffers;
25import org.eclipse.jetty.io.EndPoint;
26import org.eclipse.jetty.util.log.Log;
27import org.eclipse.jetty.util.log.Logger;
28
29
30
31/* ------------------------------------------------------------ */
32/**
33 * Parser the WebSocket protocol.
34 *
35 */
36public class WebSocketParserD08 implements WebSocketParser
37{
38 private static final Logger LOG = Log.getLogger(WebSocketParserD08.class);
39
40 public enum State {
41
42 START(0), OPCODE(1), LENGTH_7(1), LENGTH_16(2), LENGTH_63(8), MASK(4), PAYLOAD(0), DATA(0), SKIP(1), SEEK_EOF(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 private final WebSocketBuffers _buffers;
58 private final EndPoint _endp;
59 private final FrameHandler _handler;
60 private final boolean _shouldBeMasked;
61 private State _state;
62 private Buffer _buffer;
63 private byte _flags;
64 private byte _opcode;
65 private int _bytesNeeded;
66 private long _length;
67 private boolean _masked;
68 private final byte[] _mask = new byte[4];
69 private int _m;
70 private boolean _skip;
71 private boolean _fragmentFrames=true;
72
73 /* ------------------------------------------------------------ */
74 /**
75 * @param buffers The buffers to use for parsing. Only the {@link Buffers#getBuffer()} is used.
76 * This should be a direct buffer if binary data is mostly used or an indirect buffer if utf-8 data
77 * is mostly used.
78 * @param endp the endpoint
79 * @param handler the handler to notify when a parse event occurs
80 * @param shouldBeMasked whether masking should be handled
81 */
82 public WebSocketParserD08(WebSocketBuffers buffers, EndPoint endp, FrameHandler handler, boolean shouldBeMasked)
83 {
84 _buffers=buffers;
85 _endp=endp;
86 _handler=handler;
87 _shouldBeMasked=shouldBeMasked;
88 _state=State.START;
89 }
90
91 /* ------------------------------------------------------------ */
92 /**
93 * @return True if fake fragments should be created for frames larger than the buffer.
94 */
95 public boolean isFakeFragments()
96 {
97 return _fragmentFrames;
98 }
99
100 /* ------------------------------------------------------------ */
101 /**
102 * @param fakeFragments True if fake fragments should be created for frames larger than the buffer.
103 */
104 public void setFakeFragments(boolean fakeFragments)
105 {
106 _fragmentFrames = fakeFragments;
107 }
108
109 /* ------------------------------------------------------------ */
110 public boolean isBufferEmpty()
111 {
112 return _buffer==null || _buffer.length()==0;
113 }
114
115 /* ------------------------------------------------------------ */
116 public Buffer getBuffer()
117 {
118 return _buffer;
119 }
120
121 /* ------------------------------------------------------------ */
122 /** Parse to next event.
123 * Parse to the next {@link WebSocketParser.FrameHandler} event or until no more data is
124 * available. Fill data from the {@link EndPoint} only as necessary.
125 * @return An indication of progress or otherwise. -1 indicates EOF, 0 indicates
126 * that no bytes were read and no messages parsed. A positive number indicates either
127 * the bytes filled or the messages parsed.
128 */
129 public int parseNext()
130 {
131 if (_buffer==null)
132 _buffer=_buffers.getBuffer();
133
134 boolean progress=false;
135 int filled=-1;
136
137 // Loop until a datagram call back or can't fill anymore
138 while(!progress && (!_endp.isInputShutdown()||_buffer.length()>0))
139 {
140 int available=_buffer.length();
141
142 // Fill buffer if we need a byte or need length
143 while (available<(_state==State.SKIP?1:_bytesNeeded))
144 {
145 // compact to mark (set at start of data)
146 _buffer.compact();
147
148 // if no space, then the data is too big for buffer
149 if (_buffer.space() == 0)
150 {
151 // Can we send a fake frame?
152 if (_fragmentFrames && _state==State.DATA)
153 {
154 Buffer data =_buffer.get(4*(available/4));
155 _buffer.compact();
156 if (_masked)
157 {
158 if (data.array()==null)
159 data=_buffer.asMutableBuffer();
160 byte[] array = data.array();
161 final int end=data.putIndex();
162 for (int i=data.getIndex();i<end;i++)
163 array[i]^=_mask[_m++%4];
164 }
165
166 // System.err.printf("%s %s %s >>\n",TypeUtil.toHexString(_flags),TypeUtil.toHexString(_opcode),data.length());
167 _bytesNeeded-=data.length();
168 progress=true;
169 _handler.onFrame((byte)(_flags&(0xff^WebSocketConnectionD08.FLAG_FIN)), _opcode, data);
170
171 _opcode=WebSocketConnectionD08.OP_CONTINUATION;
172 }
173
174 if (_buffer.space() == 0)
175 throw new IllegalStateException("FULL: "+_state+" "+_bytesNeeded+">"+_buffer.capacity());
176 }
177
178 // catch IOExceptions (probably EOF) and try to parse what we have
179 try
180 {
181 filled=_endp.isInputShutdown()?-1:_endp.fill(_buffer);
182 available=_buffer.length();
183 // System.err.printf(">> filled %d/%d%n",filled,available);
184 if (filled<=0)
185 break;
186 }
187 catch(IOException e)
188 {
189 LOG.debug(e);
190 filled=-1;
191 break;
192 }
193 }
194 // Did we get enough?
195 if (available<(_state==State.SKIP?1:_bytesNeeded))
196 break;
197
198 // if we are here, then we have sufficient bytes to process the current state.
199 // Parse the buffer byte by byte (unless it is STATE_DATA)
200 byte b;
201 while (_state!=State.DATA && available>=(_state==State.SKIP?1:_bytesNeeded))
202 {
203 switch (_state)
204 {
205 case START:
206 _skip=false;
207 _state=_opcode==WebSocketConnectionD08.OP_CLOSE?State.SEEK_EOF:State.OPCODE;
208 _bytesNeeded=_state.getNeeds();
209 continue;
210
211 case OPCODE:
212 b=_buffer.get();
213 available--;
214 _opcode=(byte)(b&0xf);
215 _flags=(byte)(0xf&(b>>4));
216
217 if (WebSocketConnectionD08.isControlFrame(_opcode)&&!WebSocketConnectionD08.isLastFrame(_flags))
218 {
219 LOG.warn("Fragmented Control from "+_endp);
220 _handler.close(WebSocketConnectionD08.CLOSE_PROTOCOL,"Fragmented control");
221 progress=true;
222 _skip=true;
223 }
224
225 _state=State.LENGTH_7;
226 _bytesNeeded=_state.getNeeds();
227
228 continue;
229
230 case LENGTH_7:
231 b=_buffer.get();
232 available--;
233 _masked=(b&0x80)!=0;
234 b=(byte)(0x7f&b);
235
236 switch(b)
237 {
238 case 0x7f:
239 _length=0;
240 _state=State.LENGTH_63;
241 break;
242 case 0x7e:
243 _length=0;
244 _state=State.LENGTH_16;
245 break;
246 default:
247 _length=(0x7f&b);
248 _state=_masked?State.MASK:State.PAYLOAD;
249 }
250 _bytesNeeded=_state.getNeeds();
251 continue;
252
253 case LENGTH_16:
254 b=_buffer.get();
255 available--;
256 _length = _length*0x100 + (0xff&b);
257 if (--_bytesNeeded==0)
258 {
259 if (_length>_buffer.capacity() && !_fragmentFrames)
260 {
261 progress=true;
262 _handler.close(WebSocketConnectionD08.CLOSE_BADDATA,"frame size "+_length+">"+_buffer.capacity());
263 _skip=true;
264 }
265
266 _state=_masked?State.MASK:State.PAYLOAD;
267 _bytesNeeded=_state.getNeeds();
268 }
269 continue;
270
271 case LENGTH_63:
272 b=_buffer.get();
273 available--;
274 _length = _length*0x100 + (0xff&b);
275 if (--_bytesNeeded==0)
276 {
277 _bytesNeeded=(int)_length;
278 if (_length>=_buffer.capacity() && !_fragmentFrames)
279 {
280 progress=true;
281 _handler.close(WebSocketConnectionD08.CLOSE_BADDATA,"frame size "+_length+">"+_buffer.capacity());
282 _skip=true;
283 }
284
285 _state=_masked?State.MASK:State.PAYLOAD;
286 _bytesNeeded=_state.getNeeds();
287 }
288 continue;
289
290 case MASK:
291 _buffer.get(_mask,0,4);
292 _m=0;
293 available-=4;
294 _state=State.PAYLOAD;
295 _bytesNeeded=_state.getNeeds();
296 break;
297
298 case PAYLOAD:
299 _bytesNeeded=(int)_length;
300 _state=_skip?State.SKIP:State.DATA;
301 break;
302
303 case DATA:
304 break;
305
306 case SKIP:
307 int skip=Math.min(available,_bytesNeeded);
308 progress=true;
309 _buffer.skip(skip);
310 available-=skip;
311 _bytesNeeded-=skip;
312 if (_bytesNeeded==0)
313 _state=State.START;
314 break;
315
316 case SEEK_EOF:
317 progress=true;
318 _buffer.skip(available);
319 available=0;
320 break;
321 }
322 }
323
324 if (_state==State.DATA && available>=_bytesNeeded)
325 {
326 if ( _masked!=_shouldBeMasked)
327 {
328 _buffer.skip(_bytesNeeded);
329 _state=State.START;
330 progress=true;
331 _handler.close(WebSocketConnectionD08.CLOSE_PROTOCOL,"bad mask");
332 }
333 else
334 {
335 Buffer data =_buffer.get(_bytesNeeded);
336 if (_masked)
337 {
338 if (data.array()==null)
339 data=_buffer.asMutableBuffer();
340 byte[] array = data.array();
341 final int end=data.putIndex();
342 for (int i=data.getIndex();i<end;i++)
343 array[i]^=_mask[_m++%4];
344 }
345
346 // System.err.printf("%s %s %s >>\n",TypeUtil.toHexString(_flags),TypeUtil.toHexString(_opcode),data.length());
347
348 progress=true;
349 _handler.onFrame(_flags, _opcode, data);
350 _bytesNeeded=0;
351 _state=State.START;
352 }
353
354 break;
355 }
356 }
357
358 return progress?1:filled;
359 }
360
361 /* ------------------------------------------------------------ */
362 public void fill(Buffer buffer)
363 {
364 if (buffer!=null && buffer.length()>0)
365 {
366 if (_buffer==null)
367 _buffer=_buffers.getBuffer();
368
369 _buffer.put(buffer);
370 buffer.clear();
371 }
372 }
373
374 /* ------------------------------------------------------------ */
375 public void returnBuffer()
376 {
377 if (_buffer!=null && _buffer.length()==0)
378 {
379 _buffers.returnBuffer(_buffer);
380 _buffer=null;
381 }
382 }
383
384 /* ------------------------------------------------------------ */
385 @Override
386 public String toString()
387 {
388 return String.format("%s@%x state=%s buffer=%s",
389 getClass().getSimpleName(),
390 hashCode(),
391 _state,
392 _buffer);
393 }
394}