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 the endpoint
55       * @param handler the handler to notify when a parse event occurs
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          while(true)
93          {
94              int length=_buffer.length();
95  
96              // Fill buffer if we need a byte or need length
97              if (length == 0 || _state==STATE_DATA && length<_length)
98              {
99                  // compact to mark (set at start of data)
100                 _buffer.compact();
101 
102                 // if no space, then the data is too big for buffer
103                 if (_buffer.space() == 0)
104                     throw new IllegalStateException("FULL");
105 
106                 // catch IOExceptions (probably EOF) and try to parse what we have
107                 try
108                 {
109                     int filled=_endp.isOpen()?_endp.fill(_buffer):-1;
110                     if (filled<=0)
111                         return total_filled;
112                     total_filled+=filled;
113                     length=_buffer.length();
114                 }
115                 catch(IOException e)
116                 {
117                     LOG.debug(e);
118                     return total_filled>0?total_filled:-1;
119                 }
120             }
121 
122 
123             // Parse the buffer byte by byte (unless it is STATE_DATA)
124             byte b;
125             charloop: while (length-->0)
126             {
127                 switch (_state)
128                 {
129                     case STATE_START:
130                         b=_buffer.get();
131                         _opcode=b;
132                         if (_opcode<0)
133                         {
134                             _length=0;
135                             _state=STATE_LENGTH;
136                         }
137                         else
138                         {
139                             _state=STATE_SENTINEL_DATA;
140                             _buffer.mark(0);
141                         }
142                         continue;
143 
144                     case STATE_SENTINEL_DATA:
145                         b=_buffer.get();
146                         if ((b&0xff)==0xff)
147                         {
148                             _state=STATE_START;
149                             int l=_buffer.getIndex()-_buffer.markIndex()-1;
150                             _handler.onFrame((byte)0,_opcode,_buffer.sliceFromMark(l));
151                             _buffer.setMarkIndex(-1);
152                             if (_buffer.length()==0)
153                             {
154                                 _buffers.returnBuffer(_buffer);
155                                 _buffer=null;
156                             }
157                             return total_filled;
158                         }
159                         continue;
160 
161                     case STATE_LENGTH:
162                         b=_buffer.get();
163                         _length=_length<<7 | (0x7f&b);
164                         if (b>=0)
165                         {
166                             _state=STATE_DATA;
167                             _buffer.mark(0);
168                         }
169                         continue;
170 
171                     case STATE_DATA:
172                         if (_buffer.markIndex()<0)
173                         if (_buffer.length()<_length)
174                             break charloop;
175                         Buffer data=_buffer.sliceFromMark(_length);
176                         _buffer.skip(_length);
177                         _state=STATE_START;
178                         _handler.onFrame((byte)0, _opcode, data);
179 
180                         if (_buffer.length()==0)
181                         {
182                             _buffers.returnBuffer(_buffer);
183                             _buffer=null;
184                         }
185 
186                         return total_filled;
187                 }
188             }
189         }
190     }
191 
192     /* ------------------------------------------------------------ */
193     public void fill(Buffer buffer)
194     {
195         if (buffer!=null && buffer.length()>0)
196         {
197             if (_buffer==null)
198                 _buffer=_buffers.getBuffer();
199             _buffer.put(buffer);
200             buffer.clear();
201         }
202     }
203 
204 }