blob: 553978071f2d19cf2aa9f6f8b9daa56a7d4d963a [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 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}