View Javadoc

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