PageRenderTime 26ms CodeModel.GetById 21ms RepoModel.GetById 1ms app.codeStats 0ms

/jetty-8.1.5.v20120716/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketParserD08.java

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