View Javadoc

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