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