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 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 } 213