View Javadoc

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