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 }