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 }