1//
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}
311