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 total bytes filled or -1 for EOF
67       */
68      public int parseNext()
69      {
70          if (_buffer==null)
71              _buffer=_buffers.getBuffer();
72          
73          int total_filled=0;
74  
75          // Loop until an datagram call back or can't fill anymore
76          boolean progress=true;
77          while(true)
78          {
79              int length=_buffer.length();
80  
81              // Fill buffer if we need a byte or need length
82              if (length == 0 || _state==STATE_DATA && length<_length)
83              {
84                  // compact to mark (set at start of data)
85                  _buffer.compact();
86                  
87                  // if no space, then the data is too big for buffer
88                  if (_buffer.space() == 0) 
89                      throw new IllegalStateException("FULL");   
90                  
91                  // catch IOExceptions (probably EOF) and try to parse what we have
92                  try
93                  {
94                      int filled=_endp.isOpen()?_endp.fill(_buffer):-1;
95                      if (filled<=0)
96                          return total_filled;
97                      total_filled+=filled;
98                      length=_buffer.length();
99                  }
100                 catch(IOException e)
101                 {
102                     Log.debug(e);
103                     return total_filled>0?total_filled:-1;
104                 }
105             }
106 
107 
108             // Parse the buffer byte by byte (unless it is STATE_DATA)
109             byte b;
110             charloop: while (length-->0)
111             {
112                 switch (_state)
113                 {
114                     case STATE_START:
115                         b=_buffer.get();
116                         _frame=b;
117                         if (_frame<0)
118                         {
119                             _length=0;
120                             _state=STATE_LENGTH;
121                         }
122                         else
123                         {
124                             if (_utf8==null)
125                                 _utf8=new Utf8StringBuilder();
126                             _state=STATE_SENTINEL_DATA;
127                             _buffer.mark();
128                         }
129                         continue;
130 
131                     case STATE_SENTINEL_DATA:
132                         b=_buffer.get();
133                         if ((b&0xff)==0xff)
134                         {
135                             String data=_utf8.toString();
136                             _utf8.reset();
137                             _state=STATE_START;
138                             _handler.onFrame(_frame,data);
139                             _buffer.setMarkIndex(-1);
140                             if (_buffer.length()==0)
141                             {
142                                 _buffers.returnBuffer(_buffer);
143                                 _buffer=null;
144                             }
145                             return total_filled;
146                         }
147                         _utf8.append(b);
148                         continue;
149 
150                     case STATE_LENGTH:
151                         b=_buffer.get();
152                         _length=_length<<7 | (0x7f&b);
153                         if (b>=0)
154                         {
155                             _state=STATE_DATA;
156                             _buffer.mark(0);
157                         }
158                         continue;
159 
160                     case STATE_DATA:
161                         if (_buffer.markIndex()<0)
162                         if (_buffer.length()<_length)
163                             break charloop;
164                         Buffer data=_buffer.sliceFromMark(_length);
165                         _buffer.skip(_length);
166                         _state=STATE_START;
167                         _handler.onFrame(_frame,data);
168                         
169                         if (_buffer.length()==0)
170                         {
171                             _buffers.returnBuffer(_buffer);
172                             _buffer=null;
173                         }
174                         
175                         return total_filled;
176                 }
177             }
178         }
179     }
180 
181     /* ------------------------------------------------------------ */
182     public void fill(Buffer buffer)
183     {
184         if (buffer!=null && buffer.length()>0)
185         {
186             if (_buffer==null)
187                 _buffer=_buffers.getBuffer();
188             _buffer.put(buffer);
189             buffer.clear();
190         }
191         
192             
193     }
194     
195     /* ------------------------------------------------------------ */
196     /* ------------------------------------------------------------ */
197     /* ------------------------------------------------------------ */
198     public interface EventHandler
199     {
200         void onFrame(byte frame,String data);
201         void onFrame(byte frame,Buffer buffer);
202     }
203 
204 }