1// 2// ======================================================================== 3// Copyright (c) 1995-2014 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 19package org.eclipse.jetty.websocket; 20 21import java.io.IOException; 22 23import org.eclipse.jetty.io.Buffer; 24import org.eclipse.jetty.io.Buffers; 25import org.eclipse.jetty.io.EndPoint; 26import org.eclipse.jetty.util.log.Log; 27import org.eclipse.jetty.util.log.Logger; 28 29 30 31/* ------------------------------------------------------------ */ 32/** 33 * Parser the WebSocket protocol. 34 * 35 */ 36public class WebSocketParserD06 implements WebSocketParser 37{ 38 private static final Logger LOG = Log.getLogger(WebSocketParserD06.class); 39 40 public enum State { 41 42 START(0), MASK(4), OPCODE(1), LENGTH_7(1), LENGTH_16(2), LENGTH_63(8), DATA(0), SKIP(1); 43 44 int _needs; 45 46 State(int needs) 47 { 48 _needs=needs; 49 } 50 51 int getNeeds() 52 { 53 return _needs; 54 } 55 } 56 57 58 private final WebSocketBuffers _buffers; 59 private final EndPoint _endp; 60 private final FrameHandler _handler; 61 private final boolean _masked; 62 private State _state; 63 private Buffer _buffer; 64 private byte _flags; 65 private byte _opcode; 66 private int _bytesNeeded; 67 private long _length; 68 private final byte[] _mask = new byte[4]; 69 private int _m; 70 71 /* ------------------------------------------------------------ */ 72 /** 73 * @param buffers The buffers to use for parsing. Only the {@link Buffers#getBuffer()} is used. 74 * This should be a direct buffer if binary data is mostly used or an indirect buffer if utf-8 data 75 * is mostly used. 76 * @param endp the endpoint 77 * @param handler the handler to notify when a parse event occurs 78 * @param masked whether masking should be handled 79 */ 80 public WebSocketParserD06(WebSocketBuffers buffers, EndPoint endp, FrameHandler handler, boolean masked) 81 { 82 _buffers=buffers; 83 _endp=endp; 84 _handler=handler; 85 _masked=masked; 86 _state=State.START; 87 } 88 89 /* ------------------------------------------------------------ */ 90 public boolean isBufferEmpty() 91 { 92 return _buffer==null || _buffer.length()==0; 93 } 94 95 /* ------------------------------------------------------------ */ 96 public Buffer getBuffer() 97 { 98 return _buffer; 99 } 100 101 /* ------------------------------------------------------------ */ 102 /** Parse to next event. 103 * Parse to the next {@link WebSocketParser.FrameHandler} event or until no more data is 104 * available. Fill data from the {@link EndPoint} only as necessary. 105 * @return An indication of progress or otherwise. -1 indicates EOF, 0 indicates 106 * that no bytes were read and no messages parsed. A positive number indicates either 107 * the bytes filled or the messages parsed. 108 */ 109 public int parseNext() 110 { 111 if (_buffer==null) 112 _buffer=_buffers.getBuffer(); 113 114 int total_filled=0; 115 int events=0; 116 117 // Loop until an datagram call back or can't fill anymore 118 while(true) 119 { 120 int available=_buffer.length(); 121 122 // Fill buffer if we need a byte or need length 123 while (available<(_state==State.SKIP?1:_bytesNeeded)) 124 { 125 // compact to mark (set at start of data) 126 _buffer.compact(); 127 128 // if no space, then the data is too big for buffer 129 if (_buffer.space() == 0) 130 throw new IllegalStateException("FULL: "+_state+" "+_bytesNeeded+">"+_buffer.capacity()); 131 132 // catch IOExceptions (probably EOF) and try to parse what we have 133 try 134 { 135 int filled=_endp.isOpen()?_endp.fill(_buffer):-1; 136 if (filled<=0) 137 return (total_filled+events)>0?(total_filled+events):filled; 138 total_filled+=filled; 139 available=_buffer.length(); 140 } 141 catch(IOException e) 142 { 143 LOG.debug(e); 144 return (total_filled+events)>0?(total_filled+events):-1; 145 } 146 } 147 148 // if we are here, then we have sufficient bytes to process the current state. 149 150 // Parse the buffer byte by byte (unless it is STATE_DATA) 151 byte b; 152 while (_state!=State.DATA && available>=(_state==State.SKIP?1:_bytesNeeded)) 153 { 154 switch (_state) 155 { 156 case START: 157 _state=_masked?State.MASK:State.OPCODE; 158 _bytesNeeded=_state.getNeeds(); 159 continue; 160 161 case MASK: 162 _buffer.get(_mask,0,4); 163 available-=4; 164 _state=State.OPCODE; 165 _bytesNeeded=_state.getNeeds(); 166 _m=0; 167 continue; 168 169 case OPCODE: 170 b=_buffer.get(); 171 available--; 172 if (_masked) 173 b^=_mask[_m++%4]; 174 _opcode=(byte)(b&0xf); 175 _flags=(byte)(0xf&(b>>4)); 176 177 if (WebSocketConnectionD06.isControlFrame(_opcode)&&!WebSocketConnectionD06.isLastFrame(_flags)) 178 { 179 _state=State.SKIP; 180 events++; 181 _handler.close(WebSocketConnectionD06.CLOSE_PROTOCOL,"fragmented control"); 182 } 183 else 184 _state=State.LENGTH_7; 185 186 _bytesNeeded=_state.getNeeds(); 187 continue; 188 189 case LENGTH_7: 190 b=_buffer.get(); 191 available--; 192 if (_masked) 193 b^=_mask[_m++%4]; 194 switch(b) 195 { 196 case 127: 197 _length=0; 198 _state=State.LENGTH_63; 199 _bytesNeeded=_state.getNeeds(); 200 break; 201 case 126: 202 _length=0; 203 _state=State.LENGTH_16; 204 _bytesNeeded=_state.getNeeds(); 205 break; 206 default: 207 _length=(0x7f&b); 208 _bytesNeeded=(int)_length; 209 _state=State.DATA; 210 } 211 continue; 212 213 case LENGTH_16: 214 b=_buffer.get(); 215 available--; 216 if (_masked) 217 b^=_mask[_m++%4]; 218 _length = _length*0x100 + (0xff&b); 219 if (--_bytesNeeded==0) 220 { 221 _bytesNeeded=(int)_length; 222 if (_length>_buffer.capacity()) 223 { 224 _state=State.SKIP; 225 events++; 226 _handler.close(WebSocketConnectionD06.CLOSE_LARGE,"frame size "+_length+">"+_buffer.capacity()); 227 } 228 else 229 { 230 _state=State.DATA; 231 } 232 } 233 continue; 234 235 case LENGTH_63: 236 b=_buffer.get(); 237 available--; 238 if (_masked) 239 b^=_mask[_m++%4]; 240 _length = _length*0x100 + (0xff&b); 241 if (--_bytesNeeded==0) 242 { 243 _bytesNeeded=(int)_length; 244 if (_length>=_buffer.capacity()) 245 { 246 _state=State.SKIP; 247 events++; 248 _handler.close(WebSocketConnectionD06.CLOSE_LARGE,"frame size "+_length+">"+_buffer.capacity()); 249 } 250 else 251 { 252 _state=State.DATA; 253 } 254 } 255 continue; 256 257 case SKIP: 258 int skip=Math.min(available,_bytesNeeded); 259 _buffer.skip(skip); 260 available-=skip; 261 _bytesNeeded-=skip; 262 if (_bytesNeeded==0) 263 _state=State.START; 264 265 } 266 } 267 268 if (_state==State.DATA && available>=_bytesNeeded) 269 { 270 Buffer data =_buffer.get(_bytesNeeded); 271 if (_masked) 272 { 273 if (data.array()==null) 274 data=_buffer.asMutableBuffer(); 275 byte[] array = data.array(); 276 final int end=data.putIndex(); 277 for (int i=data.getIndex();i<end;i++) 278 array[i]^=_mask[_m++%4]; 279 } 280 281 // System.err.printf("%s %s %s >>\n",TypeUtil.toHexString(_flags),TypeUtil.toHexString(_opcode),data.length()); 282 events++; 283 _handler.onFrame(_flags, _opcode, data); 284 _bytesNeeded=0; 285 _state=State.START; 286 287 if (_buffer.length()==0) 288 { 289 _buffers.returnBuffer(_buffer); 290 _buffer=null; 291 } 292 293 return total_filled+events; 294 } 295 } 296 } 297 298 /* ------------------------------------------------------------ */ 299 public void fill(Buffer buffer) 300 { 301 if (buffer!=null && buffer.length()>0) 302 { 303 if (_buffer==null) 304 _buffer=_buffers.getBuffer(); 305 _buffer.put(buffer); 306 buffer.clear(); 307 } 308 } 309 310} 311