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