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