@Override public DataFrame deserialize(InputStream inputStream) throws IOException { if (this.streamChecked == null) { this.nio = inputStream.getClass().getName().endsWith("TcpNioConnection$ChannelInputStream"); this.streamAccessor = new DirectFieldAccessor(inputStream); this.streamChecked = Boolean.TRUE; } DataFrame frame = null; BasicState state = this.getState(inputStream); if (state != null) { frame = state.getPendingFrame(); } while (frame == null || (frame.getPayload() == null && frame.getBinary() == null)) { frame = doDeserialize(inputStream, frame); if (frame.getPayload() == null && frame.getBinary() == null) { state.setPendingFrame(frame); } } return frame; }
private DataFrame doDeserialize(InputStream inputStream, DataFrame protoFrame) throws IOException { List<DataFrame> headers = checkStreaming(inputStream); if (headers != null) { return headers.get(0); } int bite; if (logger.isDebugEnabled()) { logger.debug("Available to read:" + inputStream.available()); } boolean done = false; int len = 0; int n = 0; int dataInx = 0; byte[] buffer = null; boolean fin = false; boolean ping = false; boolean pong = false; boolean close = false; boolean binary = false; boolean invalid = false; String invalidText = null; boolean fragmentedControl = false; int lenBytes = 0; byte[] mask = new byte[4]; int maskInx = 0; int rsv = 0; while (!done) { bite = inputStream.read(); // logger.debug("Read:" + Integer.toHexString(bite)); if (this.nio) { bite = checkclosed(bite, inputStream); } if (bite < 0 && n == 0) { throw new SoftEndOfStreamException("Stream closed between payloads"); } checkClosure(bite); switch (n++) { case 0: fin = (bite & 0x80) > 0; rsv = (bite & 0x70) >> 4; bite &= 0x0f; switch (bite) { case 0x00: logger.debug("Continuation, fin=" + fin); if (protoFrame == null) { invalid = true; invalidText = "Unexpected continuation frame"; } else { binary = protoFrame.getType() == WebSocketFrame.TYPE_DATA_BINARY; } this.getState(inputStream).setPendingFrame(null); break; case 0x01: logger.debug("Text, fin=" + fin); if (protoFrame != null) { invalid = true; invalidText = "Expected continuation frame"; } break; case 0x02: logger.debug("Binary, fin=" + fin); if (protoFrame != null) { invalid = true; invalidText = "Expected continuation frame"; } binary = true; break; case 0x08: logger.debug("Close, fin=" + fin); fragmentedControl = !fin; close = true; break; case 0x09: ping = true; binary = true; fragmentedControl = !fin; logger.debug("Ping, fin=" + fin); break; case 0x0a: pong = true; fragmentedControl = !fin; logger.debug("Pong, fin=" + fin); break; case 0x03: case 0x04: case 0x05: case 0x06: case 0x07: case 0x0b: case 0x0c: case 0x0d: case 0x0e: case 0x0f: invalid = true; invalidText = "Reserved opcode " + Integer.toHexString(bite); break; default: throw new IOException("Unexpected opcode " + Integer.toHexString(bite)); } break; case 1: if (this.server) { if ((bite & 0x80) == 0) { throw new IOException("Illegal: Expected masked data from client"); } bite &= 0x7f; } if ((bite & 0x80) > 0) { throw new IOException("Illegal: Received masked data from server"); } if (bite < 126) { len = bite; buffer = new byte[len]; } else if (bite == 126) { lenBytes = 2; } else { lenBytes = 8; } break; case 2: case 3: case 4: case 5: if (lenBytes > 4 && bite != 0) { throw new IOException("Max supported length exceeded"); } case 6: if (lenBytes > 3 && (bite & 0x80) > 0) { throw new IOException("Max supported length exceeded"); } case 7: case 8: case 9: if (lenBytes-- > 0) { len = len << 8 | (bite & 0xff); if (lenBytes == 0) { buffer = new byte[len]; } break; } default: if (this.server && maskInx < 4) { mask[maskInx++] = (byte) bite; } else { if (this.server) { bite ^= mask[dataInx % 4]; } buffer[dataInx++] = (byte) bite; } done = (server ? maskInx == 4 : true) && dataInx >= len; } } ; WebSocketFrame frame; if (fragmentedControl) { frame = new WebSocketFrame( WebSocketFrame.TYPE_FRAGMENTED_CONTROL, "Fragmented control frame", buffer); } else if (invalid) { frame = new WebSocketFrame(WebSocketFrame.TYPE_INVALID, invalidText, buffer); } else if (!fin) { List<byte[]> fragments = this.getState(inputStream).getFragments(); fragments.add(buffer); logger.debug("Fragment"); return new WebSocketFrame( binary ? WebSocketFrame.TYPE_DATA_BINARY : WebSocketFrame.TYPE_DATA, (String) null); } else if (ping) { frame = new WebSocketFrame(WebSocketFrame.TYPE_PING, buffer); } else if (pong) { String data = new String(buffer, "UTF-8"); frame = new WebSocketFrame(WebSocketFrame.TYPE_PONG, data); } else if (close) { String data = new String(buffer, "UTF-8"); if (data.length() >= 2) { data = data.substring(2); } WebSocketFrame closeFrame = new WebSocketFrame(WebSocketFrame.TYPE_CLOSE, data); short status = 1000; if (buffer.length >= 2) { status = (short) ((buffer[0] << 8) | (buffer[1] & 0xff)); closeFrame.setStatus(status); } if (buffer.length == 1 || buffer.length > 125 || (buffer.length > 2 && !validateUtf8IfNecessary(buffer, 2, data)) || status < 1000 || INVALID_STATUS.contains(status) || (status >= 1016 && status < 3000) || status >= 5000) { // Simply close in this case; no close reply ((WebSocketState) this.getState(inputStream)).setCloseInitiated(true); } frame = closeFrame; } else { List<byte[]> fragments = this.getState(inputStream).getFragments(); if (fragments.size() == 0) { if (binary) { frame = new WebSocketFrame(WebSocketFrame.TYPE_DATA_BINARY, buffer); } else { String data = new String(buffer, "UTF-8"); if (!validateUtf8IfNecessary(buffer, 0, data)) { frame = new WebSocketFrame(WebSocketFrame.TYPE_INVALID_UTF8, "Invalid UTF-8", buffer); } else { frame = new WebSocketFrame(WebSocketFrame.TYPE_DATA, data); } } } else { fragments.add(buffer); int utf8Len = 0; for (byte[] fragment : fragments) { utf8Len += fragment.length; } byte[] reconstructed = new byte[utf8Len]; int utf8Pos = 0; for (byte[] fragment : fragments) { System.arraycopy(fragment, 0, reconstructed, utf8Pos, fragment.length); utf8Pos += fragment.length; } fragments.clear(); if (binary) { frame = new WebSocketFrame(WebSocketFrame.TYPE_DATA_BINARY, reconstructed); } else { String data = new String(reconstructed, "UTF-8"); if (!validateUtf8IfNecessary(reconstructed, 0, data)) { frame = new WebSocketFrame( WebSocketFrame.TYPE_INVALID_UTF8, "Invalid UTF-8", reconstructed); } else { frame = new WebSocketFrame(WebSocketFrame.TYPE_DATA, data); } } } } if (rsv > 0) { frame.setRsv(rsv); } return frame; }