finalFlag = (b & 0x80) != 0;
int tmpOp = b & 0x0F;
if (opcode != -1 && tmpOp != opcode) {
// TODO ping frame in fragmented text frame
throw new ProtocolException("opcode mismatch: pre: " + opcode + ", now: "
+ tmpOp);
}
opcode = tmpOp;
state = State.READ_LENGTH;
break;
case READ_LENGTH:
b = buffer.get(); // MASK, PAYLOAD LEN 1
boolean masked = (b & 0x80) != 0;
if (!masked) {
throw new ProtocolException("unmasked client to server frame");
}
payloadLength = b & 0x7F;
if (payloadLength == 126) {
state = State.READ_2_LENGTH;
} else if (payloadLength == 127) {
state = State.READ_8_LENGTH;
} else {
state = State.MASKING_KEY;
}
break;
case READ_2_LENGTH:
if (isAvailable(buffer, 2)) {
payloadLength = tmpBuffer.getShort() & 0xFFFF;
tmpBuffer.clear();
if (payloadLength < 126) {
throw new ProtocolException(
"invalid data frame length (not using minimal length encoding)");
}
state = State.MASKING_KEY;
}
break;
case READ_8_LENGTH:
if (isAvailable(buffer, 8)) {
long length = tmpBuffer.getLong();
tmpBuffer.clear();
// if negative, that too big, drop it.
if (length < 65536) {
throw new ProtocolException("invalid data frame length. max payload length 4M");
}
abortIfTooLarge(length);
payloadLength = (int) length;
state = State.MASKING_KEY;
}
break; // wait for more data from TCP
case MASKING_KEY:
if (isAvailable(buffer, 4)) {
maskingKey = tmpBuffer.getInt();
tmpBuffer.clear();
if (content == null) {
content = new byte[payloadLength];
} else if (payloadLength > 0) {
abortIfTooLarge(content.length + payloadLength);
/*
* TODO if an attacker sent many fragmented frames, only one
* byte of data per frame, server end up reallocate many
* times. may not be a problem
*/
// resize
content = Arrays.copyOf(content, content.length + payloadLength);
}
framePayloadIndex = 0; // reset
state = State.PAYLOAD;
// No break. since payloadLength can be 0
} else {
break; // wait for more data from TCP
}
case PAYLOAD:
int read = Math.min(buffer.remaining(), payloadLength - payloadRead);
if (read > 0) {
buffer.get(content, idx, read);
byte[] mask = ByteBuffer.allocate(4).putInt(maskingKey).array();
for (int i = 0; i < read; i++) {
content[i + idx] = (byte) (content[i + idx] ^ mask[(framePayloadIndex + i) % 4]);
}
payloadRead += read;
idx += read;
}
framePayloadIndex += read;
// all read (this frame)
if (payloadRead == payloadLength) {
if (finalFlag) {
switch (opcode) {
case OPCODE_TEXT:
return new Frame.TextFrame(content);
case OPCODE_BINARY:
return new Frame.BinaryFrame(content);
case OPCODE_PING:
return new Frame.PingFrame(content);
case OPCODE_PONG:
return new Frame.PongFrame(content);
case OPCODE_CLOSE:
return new Frame.CloseFrame(content);
default:
throw new ProtocolException("not impl for opcode: " + opcode);
}
} else {
state = State.FRAME_START;
payloadRead = 0;
}