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 }