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 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}
395