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 total bytes filled or -1 for EOF
67 */
68 public int parseNext()
69 {
70 if (_buffer==null)
71 _buffer=_buffers.getBuffer();
72
73 int total_filled=0;
74
75 // Loop until an datagram call back or can't fill anymore
76 boolean progress=true;
77 while(true)
78 {
79 int length=_buffer.length();
80
81 // Fill buffer if we need a byte or need length
82 if (length == 0 || _state==STATE_DATA && length<_length)
83 {
84 // compact to mark (set at start of data)
85 _buffer.compact();
86
87 // if no space, then the data is too big for buffer
88 if (_buffer.space() == 0)
89 throw new IllegalStateException("FULL");
90
91 // catch IOExceptions (probably EOF) and try to parse what we have
92 try
93 {
94 int filled=_endp.isOpen()?_endp.fill(_buffer):-1;
95 if (filled<=0)
96 return total_filled;
97 total_filled+=filled;
98 length=_buffer.length();
99 }
100 catch(IOException e)
101 {
102 Log.debug(e);
103 return total_filled>0?total_filled:-1;
104 }
105 }
106
107
108 // Parse the buffer byte by byte (unless it is STATE_DATA)
109 byte b;
110 charloop: while (length-->0)
111 {
112 switch (_state)
113 {
114 case STATE_START:
115 b=_buffer.get();
116 _frame=b;
117 if (_frame<0)
118 {
119 _length=0;
120 _state=STATE_LENGTH;
121 }
122 else
123 {
124 if (_utf8==null)
125 _utf8=new Utf8StringBuilder();
126 _state=STATE_SENTINEL_DATA;
127 _buffer.mark();
128 }
129 continue;
130
131 case STATE_SENTINEL_DATA:
132 b=_buffer.get();
133 if ((b&0xff)==0xff)
134 {
135 String data=_utf8.toString();
136 _utf8.reset();
137 _state=STATE_START;
138 _handler.onFrame(_frame,data);
139 _buffer.setMarkIndex(-1);
140 if (_buffer.length()==0)
141 {
142 _buffers.returnBuffer(_buffer);
143 _buffer=null;
144 }
145 return total_filled;
146 }
147 _utf8.append(b);
148 continue;
149
150 case STATE_LENGTH:
151 b=_buffer.get();
152 _length=_length<<7 | (0x7f&b);
153 if (b>=0)
154 {
155 _state=STATE_DATA;
156 _buffer.mark(0);
157 }
158 continue;
159
160 case STATE_DATA:
161 if (_buffer.markIndex()<0)
162 if (_buffer.length()<_length)
163 break charloop;
164 Buffer data=_buffer.sliceFromMark(_length);
165 _buffer.skip(_length);
166 _state=STATE_START;
167 _handler.onFrame(_frame,data);
168
169 if (_buffer.length()==0)
170 {
171 _buffers.returnBuffer(_buffer);
172 _buffer=null;
173 }
174
175 return total_filled;
176 }
177 }
178 }
179 }
180
181 /* ------------------------------------------------------------ */
182 public void fill(Buffer buffer)
183 {
184 if (buffer!=null && buffer.length()>0)
185 {
186 if (_buffer==null)
187 _buffer=_buffers.getBuffer();
188 _buffer.put(buffer);
189 buffer.clear();
190 }
191
192
193 }
194
195 /* ------------------------------------------------------------ */
196 /* ------------------------------------------------------------ */
197 /* ------------------------------------------------------------ */
198 public interface EventHandler
199 {
200 void onFrame(byte frame,String data);
201 void onFrame(byte frame,Buffer buffer);
202 }
203
204 }