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