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