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 WebSocketParserRFC6455 implements WebSocketParser 37{ 38 private static final Logger LOG = Log.getLogger(WebSocketParserRFC6455.class); 39 40 public enum State { 41 42 START(0), OPCODE(1), LENGTH_7(1), LENGTH_16(2), LENGTH_63(8), MASK(4), PAYLOAD(0), DATA(0), SKIP(1), SEEK_EOF(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 private final WebSocketBuffers _buffers; 58 private final EndPoint _endp; 59 private final FrameHandler _handler; 60 private final boolean _shouldBeMasked; 61 private State _state; 62 private Buffer _buffer; 63 private byte _flags; 64 private byte _opcode; 65 private int _bytesNeeded; 66 private long _length; 67 private boolean _masked; 68 private final byte[] _mask = new byte[4]; 69 private int _m; 70 private boolean _skip; 71 private boolean _fragmentFrames=true; 72 73 /* ------------------------------------------------------------ */ 74 /** 75 * @param buffers The buffers to use for parsing. Only the {@link Buffers#getBuffer()} is used. 76 * This should be a direct buffer if binary data is mostly used or an indirect buffer if utf-8 data 77 * is mostly used. 78 * @param endp the endpoint 79 * @param handler the handler to notify when a parse event occurs 80 * @param shouldBeMasked whether masking should be handled 81 */ 82 public WebSocketParserRFC6455(WebSocketBuffers buffers, EndPoint endp, FrameHandler handler, boolean shouldBeMasked) 83 { 84 _buffers=buffers; 85 _endp=endp; 86 _handler=handler; 87 _shouldBeMasked=shouldBeMasked; 88 _state=State.START; 89 } 90 91 /* ------------------------------------------------------------ */ 92 /** 93 * @return True if fake fragments should be created for frames larger than the buffer. 94 */ 95 public boolean isFakeFragments() 96 { 97 return _fragmentFrames; 98 } 99 100 /* ------------------------------------------------------------ */ 101 /** 102 * @param fakeFragments True if fake fragments should be created for frames larger than the buffer. 103 */ 104 public void setFakeFragments(boolean fakeFragments) 105 { 106 _fragmentFrames = fakeFragments; 107 } 108 109 /* ------------------------------------------------------------ */ 110 public boolean isBufferEmpty() 111 { 112 return _buffer==null || _buffer.length()==0; 113 } 114 115 /* ------------------------------------------------------------ */ 116 public Buffer getBuffer() 117 { 118 return _buffer; 119 } 120 121 /* ------------------------------------------------------------ */ 122 /** Parse to next event. 123 * Parse to the next {@link WebSocketParser.FrameHandler} event or until no more data is 124 * available. Fill data from the {@link EndPoint} only as necessary. 125 * @return An indication of progress or otherwise. -1 indicates EOF, 0 indicates 126 * that no bytes were read and no messages parsed. A positive number indicates either 127 * the bytes filled or the messages parsed. 128 */ 129 public int parseNext() 130 { 131 if (_buffer==null) 132 _buffer=_buffers.getBuffer(); 133 134 boolean progress=false; 135 int filled=-1; 136 137 // Loop until a datagram call back or can't fill anymore 138 while(!progress && (!_endp.isInputShutdown()||_buffer.length()>0)) 139 { 140 int available=_buffer.length(); 141 142 // Fill buffer if we need a byte or need length 143 while (available<(_state==State.SKIP?1:_bytesNeeded)) 144 { 145 // compact to mark (set at start of data) 146 _buffer.compact(); 147 148 // if no space, then the data is too big for buffer 149 if (_buffer.space() == 0) 150 { 151 // Can we send a fake frame? 152 if (_fragmentFrames && _state==State.DATA) 153 { 154 Buffer data =_buffer.get(4*(available/4)); 155 _buffer.compact(); 156 if (_masked) 157 { 158 if (data.array()==null) 159 data=_buffer.asMutableBuffer(); 160 byte[] array = data.array(); 161 final int end=data.putIndex(); 162 for (int i=data.getIndex();i<end;i++) 163 array[i]^=_mask[_m++%4]; 164 } 165 166 // System.err.printf("%s %s %s >>\n",TypeUtil.toHexString(_flags),TypeUtil.toHexString(_opcode),data.length()); 167 _bytesNeeded-=data.length(); 168 progress=true; 169 _handler.onFrame((byte)(_flags&(0xff^WebSocketConnectionRFC6455.FLAG_FIN)), _opcode, data); 170 171 _opcode=WebSocketConnectionRFC6455.OP_CONTINUATION; 172 } 173 174 if (_buffer.space() == 0) 175 throw new IllegalStateException("FULL: "+_state+" "+_bytesNeeded+">"+_buffer.capacity()); 176 } 177 178 // catch IOExceptions (probably EOF) and try to parse what we have 179 try 180 { 181 filled=_endp.isInputShutdown()?-1:_endp.fill(_buffer); 182 available=_buffer.length(); 183 // System.err.printf(">> filled %d/%d%n",filled,available); 184 if (filled<=0) 185 break; 186 } 187 catch(IOException e) 188 { 189 LOG.debug(e); 190 filled=-1; 191 break; 192 } 193 } 194 // Did we get enough? 195 if (available<(_state==State.SKIP?1:_bytesNeeded)) 196 break; 197 198 // if we are here, then we have sufficient bytes to process the current state. 199 // Parse the buffer byte by byte (unless it is STATE_DATA) 200 byte b; 201 while (_state!=State.DATA && available>=(_state==State.SKIP?1:_bytesNeeded)) 202 { 203 switch (_state) 204 { 205 case START: 206 _skip=false; 207 _state=_opcode==WebSocketConnectionRFC6455.OP_CLOSE?State.SEEK_EOF:State.OPCODE; 208 _bytesNeeded=_state.getNeeds(); 209 continue; 210 211 case OPCODE: 212 b=_buffer.get(); 213 available--; 214 _opcode=(byte)(b&0xf); 215 _flags=(byte)(0xf&(b>>4)); 216 217 if (WebSocketConnectionRFC6455.isControlFrame(_opcode)&&!WebSocketConnectionRFC6455.isLastFrame(_flags)) 218 { 219 LOG.warn("Fragmented Control from "+_endp); 220 _handler.close(WebSocketConnectionRFC6455.CLOSE_PROTOCOL,"Fragmented control"); 221 progress=true; 222 _skip=true; 223 } 224 225 _state=State.LENGTH_7; 226 _bytesNeeded=_state.getNeeds(); 227 228 continue; 229 230 case LENGTH_7: 231 b=_buffer.get(); 232 available--; 233 _masked=(b&0x80)!=0; 234 b=(byte)(0x7f&b); 235 236 switch(b) 237 { 238 case 0x7f: 239 _length=0; 240 _state=State.LENGTH_63; 241 break; 242 case 0x7e: 243 _length=0; 244 _state=State.LENGTH_16; 245 break; 246 default: 247 _length=(0x7f&b); 248 _state=_masked?State.MASK:State.PAYLOAD; 249 } 250 _bytesNeeded=_state.getNeeds(); 251 continue; 252 253 case LENGTH_16: 254 b=_buffer.get(); 255 available--; 256 _length = _length*0x100 + (0xff&b); 257 if (--_bytesNeeded==0) 258 { 259 if (_length>_buffer.capacity() && !_fragmentFrames) 260 { 261 progress=true; 262 _handler.close(WebSocketConnectionRFC6455.CLOSE_POLICY_VIOLATION,"frame size "+_length+">"+_buffer.capacity()); 263 _skip=true; 264 } 265 266 _state=_masked?State.MASK:State.PAYLOAD; 267 _bytesNeeded=_state.getNeeds(); 268 } 269 continue; 270 271 case LENGTH_63: 272 b=_buffer.get(); 273 available--; 274 _length = _length*0x100 + (0xff&b); 275 if (--_bytesNeeded==0) 276 { 277 _bytesNeeded=(int)_length; 278 if (_length>=_buffer.capacity() && !_fragmentFrames) 279 { 280 progress=true; 281 _handler.close(WebSocketConnectionRFC6455.CLOSE_POLICY_VIOLATION,"frame size "+_length+">"+_buffer.capacity()); 282 _skip=true; 283 } 284 285 _state=_masked?State.MASK:State.PAYLOAD; 286 _bytesNeeded=_state.getNeeds(); 287 } 288 continue; 289 290 case MASK: 291 _buffer.get(_mask,0,4); 292 _m=0; 293 available-=4; 294 _state=State.PAYLOAD; 295 _bytesNeeded=_state.getNeeds(); 296 break; 297 298 case PAYLOAD: 299 _bytesNeeded=(int)_length; 300 _state=_skip?State.SKIP:State.DATA; 301 break; 302 303 case DATA: 304 break; 305 306 case SKIP: 307 int skip=Math.min(available,_bytesNeeded); 308 progress=true; 309 _buffer.skip(skip); 310 available-=skip; 311 _bytesNeeded-=skip; 312 if (_bytesNeeded==0) 313 _state=State.START; 314 break; 315 316 case SEEK_EOF: 317 progress=true; 318 _buffer.skip(available); 319 available=0; 320 break; 321 } 322 } 323 324 if (_state==State.DATA && available>=_bytesNeeded) 325 { 326 if ( _masked!=_shouldBeMasked) 327 { 328 _buffer.skip(_bytesNeeded); 329 _state=State.START; 330 progress=true; 331 _handler.close(WebSocketConnectionRFC6455.CLOSE_PROTOCOL,"Not masked"); 332 } 333 else 334 { 335 Buffer data =_buffer.get(_bytesNeeded); 336 if (_masked) 337 { 338 if (data.array()==null) 339 data=_buffer.asMutableBuffer(); 340 byte[] array = data.array(); 341 final int end=data.putIndex(); 342 for (int i=data.getIndex();i<end;i++) 343 array[i]^=_mask[_m++%4]; 344 } 345 346 // System.err.printf("%s %s %s >>\n",TypeUtil.toHexString(_flags),TypeUtil.toHexString(_opcode),data.length()); 347 348 progress=true; 349 _handler.onFrame(_flags, _opcode, data); 350 _bytesNeeded=0; 351 _state=State.START; 352 } 353 354 break; 355 } 356 } 357 358 return progress?1:filled; 359 } 360 361 /* ------------------------------------------------------------ */ 362 public void fill(Buffer buffer) 363 { 364 if (buffer!=null && buffer.length()>0) 365 { 366 if (_buffer==null) 367 _buffer=_buffers.getBuffer(); 368 369 _buffer.put(buffer); 370 buffer.clear(); 371 } 372 } 373 374 /* ------------------------------------------------------------ */ 375 public void returnBuffer() 376 { 377 if (_buffer!=null && _buffer.length()==0) 378 { 379 _buffers.returnBuffer(_buffer); 380 _buffer=null; 381 } 382 } 383 384 /* ------------------------------------------------------------ */ 385 @Override 386 public String toString() 387 { 388 return String.format("%s@%x state=%s buffer=%s", 389 getClass().getSimpleName(), 390 hashCode(), 391 _state, 392 _buffer); 393 } 394} 395