View Javadoc

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.Utf8StringBuilder;
22  import org.eclipse.jetty.util.log.Log;
23  
24  
25  
26  /* ------------------------------------------------------------ */
27  /**
28   * Parser the WebSocket protocol.
29   *
30   */
31  public class WebSocketParserD01 implements WebSocketParser
32  {    
33      public enum State { 
34          START(1), LENGTH_7(2), LENGTH_16(4), LENGTH_63(10), DATA(10);
35  
36          int _minSize;
37  
38          State(int minSize)
39          {
40              _minSize=minSize;
41          }
42  
43          int getMinSize()
44          {
45              return _minSize;
46          }
47      };
48  
49  
50      private final WebSocketBuffers _buffers;
51      private final EndPoint _endp;
52      private final FrameHandler _handler;
53      private State _state=State.START;
54      private Buffer _buffer;
55      private boolean _more;
56      private byte _flags;
57      private byte _opcode;
58      private int _count;
59      private long _length;
60      private Utf8StringBuilder _utf8;
61  
62      /* ------------------------------------------------------------ */
63      /**
64       * @param buffers The buffers to use for parsing.  Only the {@link Buffers#getBuffer()} is used.
65       * This should be a direct buffer if binary data is mostly used or an indirect buffer if utf-8 data
66       * is mostly used.
67       * @param endp
68       * @param handler
69       */
70      public WebSocketParserD01(WebSocketBuffers buffers, EndPoint endp, FrameHandler handler)
71      {
72          _buffers=buffers;
73          _endp=endp;
74          _handler=handler;
75      }
76  
77      /* ------------------------------------------------------------ */
78      public boolean isBufferEmpty()
79      {
80          return _buffer==null || _buffer.length()==0;
81      }
82  
83      /* ------------------------------------------------------------ */
84      public Buffer getBuffer()
85      {
86          return _buffer;
87      }
88  
89      /* ------------------------------------------------------------ */
90      /** Parse to next event.
91       * Parse to the next {@link WebSocketParser.FrameHandler} event or until no more data is
92       * available. Fill data from the {@link EndPoint} only as necessary.
93       * @return An indication of progress or otherwise. -1 indicates EOF, 0 indicates
94       * that no bytes were read and no messages parsed. A positive number indicates either
95       * the bytes filled or the messages parsed.
96       */
97      public int parseNext()
98      {
99          if (_buffer==null)
100             _buffer=_buffers.getBuffer();
101 
102         int total_filled=0;
103 
104         // Loop until an datagram call back or can't fill anymore
105         while(true)
106         {
107             int available=_buffer.length();
108 
109             // Fill buffer if we need a byte or need length
110             if (available < _state.getMinSize() || _state==State.DATA && available<_count)
111             {
112                 // compact to mark (set at start of data)
113                 _buffer.compact();
114 
115                 // if no space, then the data is too big for buffer
116                 if (_buffer.space() == 0)
117                     throw new IllegalStateException("FULL");
118 
119                 // catch IOExceptions (probably EOF) and try to parse what we have
120                 try
121                 {
122                     int filled=_endp.isOpen()?_endp.fill(_buffer):-1;
123                     if (filled<=0)
124                         return total_filled;
125                     total_filled+=filled;
126                     available=_buffer.length();
127                 }
128                 catch(IOException e)
129                 {
130                     Log.debug(e);
131                     return total_filled>0?total_filled:-1;
132                 }
133             }
134 
135             // Parse the buffer byte by byte (unless it is STATE_DATA)
136             byte b;
137             while (_state!=State.DATA && available-->0)
138             {
139                 switch (_state)
140                 {
141                     case START:
142                         b=_buffer.get();
143                         _opcode=(byte)(b&0xf);
144                         _flags=(byte)(b>>4);
145                         _more=(_flags&8)!=0;
146                         _state=State.LENGTH_7;
147                         continue;
148 
149                     case LENGTH_7:
150                         b=_buffer.get();
151                         switch(b)
152                         {
153                             case 127:
154                                 _length=0;
155                                 _count=8;
156                                 _state=State.LENGTH_63;
157                                 break;
158                             case 126:
159                                 _length=0;
160                                 _count=2;
161                                 _state=State.LENGTH_16;
162                                 break;
163                             default:
164                                 _length=(0x7f&b);
165                                 _count=(int)_length;
166                                 _state=State.DATA; 
167                         }
168                         continue;
169 
170                     case LENGTH_16:
171                         b=_buffer.get();
172                         _length = _length<<8 | b;
173                         if (--_count==0)
174                         {
175                             if (_length>=_buffer.capacity()-4)
176                                 throw new IllegalStateException("TOO LARGE");
177                             _count=(int)_length;
178                             _state=State.DATA;
179                         }
180                         continue;
181 
182                     case LENGTH_63:
183                         b=_buffer.get();
184                         _length = _length<<8 | b;
185                         if (--_count==0)
186                         {
187                             if (_length>=_buffer.capacity()-10)
188                                 throw new IllegalStateException("TOO LARGE");
189                             _count=(int)_length;
190                             _state=State.DATA;
191                         }
192                         continue;
193                 }
194             }
195 
196             if (_state==State.DATA && available>=_count)
197             {
198                 _handler.onFrame(_more,_flags, _opcode, _buffer.get(_count));
199                 _count=0;
200                 _state=State.START;
201 
202                 if (_buffer.length()==0)
203                 {
204                     _buffers.returnBuffer(_buffer);
205                     _buffer=null;
206                 }
207 
208                 return total_filled;
209 
210             }
211         }
212     }
213 
214     /* ------------------------------------------------------------ */
215     public void fill(Buffer buffer)
216     {
217         if (buffer!=null && buffer.length()>0)
218         {
219             if (_buffer==null)
220                 _buffer=_buffers.getBuffer();
221             _buffer.put(buffer);
222             buffer.clear();
223         }
224     }
225 
226 }