use of io.crossbar.autobahn.websocket.utils.Utf8Validator in project autobahn-java by crossbario.
the class WebSocketReader method processData.
/**
* Process incoming WebSockets data (after handshake).
*/
private boolean processData() throws Exception {
// outside frame?
if (mFrameHeader == null) {
// need at least 2 bytes from WS frame header to start processing
if (mPosition >= 2) {
byte b0 = mMessageData[0];
boolean fin = (b0 & 0x80) != 0;
int rsv = (b0 & 0x70) >> 4;
int opcode = b0 & 0x0f;
byte b1 = mMessageData[1];
boolean masked = (b1 & 0x80) != 0;
int payload_len1 = b1 & 0x7f;
if (rsv != 0) {
throw new WebSocketException("RSV != 0 and no extension negotiated");
}
if (masked) {
// currently, we don't allow this. need to see whats the final spec.
throw new WebSocketException("masked server frame");
}
if (opcode > 7) {
// control frame
if (!fin) {
throw new WebSocketException("fragmented control frame");
}
if (payload_len1 > 125) {
throw new WebSocketException("control frame with payload length > 125 octets");
}
if (opcode != 8 && opcode != 9 && opcode != 10) {
throw new WebSocketException("control frame using reserved opcode " + opcode);
}
if (opcode == 8 && payload_len1 == 1) {
throw new WebSocketException("received close control frame with payload len 1");
}
} else {
// message frame
if (opcode != 0 && opcode != 1 && opcode != 2) {
throw new WebSocketException("data frame using reserved opcode " + opcode);
}
if (!mInsideMessage && opcode == 0) {
throw new WebSocketException("received continuation data frame outside fragmented message");
}
if (mInsideMessage && opcode != 0) {
throw new WebSocketException("received non-continuation data frame while inside fragmented message");
}
}
int mask_len = masked ? 4 : 0;
int header_len;
if (payload_len1 < 126) {
header_len = 2 + mask_len;
} else if (payload_len1 == 126) {
header_len = 2 + 2 + mask_len;
} else if (payload_len1 == 127) {
header_len = 2 + 8 + mask_len;
} else {
// should not arrive here
throw new Exception("logic error");
}
// continue when complete frame header is available
if (mPosition >= header_len) {
// determine frame payload length
int i = 2;
long payload_len;
if (payload_len1 == 126) {
payload_len = ((0xff & mMessageData[i]) << 8) | (0xff & mMessageData[i + 1]);
if (payload_len < 126) {
throw new WebSocketException("invalid data frame length (not using minimal length encoding)");
}
i += 2;
} else if (payload_len1 == 127) {
if ((0x80 & mMessageData[i]) != 0) {
throw new WebSocketException("invalid data frame length (> 2^63)");
}
payload_len = ((long) (0xff & mMessageData[i]) << 56) | ((long) (0xff & mMessageData[i + 1]) << 48) | ((long) (0xff & mMessageData[i + 2]) << 40) | ((long) (0xff & mMessageData[i + 3]) << 32) | ((long) (0xff & mMessageData[i + 4]) << 24) | ((long) (0xff & mMessageData[i + 5]) << 16) | ((long) (0xff & mMessageData[i + 6]) << 8) | ((long) (0xff & mMessageData[i + 7]));
if (payload_len < 65536) {
throw new WebSocketException("invalid data frame length (not using minimal length encoding)");
}
i += 8;
} else {
payload_len = payload_len1;
}
// immediately bail out on frame too large
if (payload_len > mOptions.getMaxFramePayloadSize()) {
throw new WebSocketException("frame payload too large");
}
// save frame header metadata
mFrameHeader = new FrameHeader();
mFrameHeader.mOpcode = opcode;
mFrameHeader.mFin = fin;
mFrameHeader.mReserved = rsv;
mFrameHeader.mPayloadLen = (int) payload_len;
mFrameHeader.mHeaderLen = header_len;
mFrameHeader.mTotalLen = mFrameHeader.mHeaderLen + mFrameHeader.mPayloadLen;
if (masked) {
mFrameHeader.mMask = new byte[4];
for (int j = 0; j < 4; ++j) {
mFrameHeader.mMask[i] = (byte) (0xff & mMessageData[i + j]);
}
i += 4;
} else {
mFrameHeader.mMask = null;
}
// continue processing when payload empty or completely buffered
return mFrameHeader.mPayloadLen == 0 || mPosition >= mFrameHeader.mTotalLen;
} else {
// need more data
return false;
}
} else {
// need more data
return false;
}
} else {
// see if we buffered complete frame
if (mPosition >= mFrameHeader.mTotalLen) {
// cut out frame payload
byte[] framePayload = null;
if (mFrameHeader.mPayloadLen > 0) {
framePayload = new byte[mFrameHeader.mPayloadLen];
System.arraycopy(mMessageData, mFrameHeader.mHeaderLen, framePayload, 0, mFrameHeader.mPayloadLen);
}
mMessageData = Arrays.copyOfRange(mMessageData, mFrameHeader.mTotalLen, mMessageData.length + mFrameHeader.mTotalLen);
mPosition -= mFrameHeader.mTotalLen;
if (mFrameHeader.mOpcode > 7) {
// control frame
if (mFrameHeader.mOpcode == 8) {
// CLOSE_STATUS_CODE_NULL : no status code received
int code = 1005;
String reason = null;
if (mFrameHeader.mPayloadLen >= 2) {
// parse and check close code - see http://tools.ietf.org/html/rfc6455#section-7.4
code = (framePayload[0] & 0xff) * 256 + (framePayload[1] & 0xff);
if (code < 1000 || (code >= 1000 && code <= 2999 && code != 1000 && code != 1001 && code != 1002 && code != 1003 && code != 1007 && code != 1008 && code != 1009 && code != 1010 && code != 1011) || code >= 5000) {
throw new WebSocketException("invalid close code " + code);
}
// parse and check close reason
if (mFrameHeader.mPayloadLen > 2) {
byte[] ra = new byte[mFrameHeader.mPayloadLen - 2];
System.arraycopy(framePayload, 2, ra, 0, mFrameHeader.mPayloadLen - 2);
Utf8Validator val = new Utf8Validator();
val.validate(ra);
if (!val.isValid()) {
throw new WebSocketException("invalid close reasons (not UTF-8)");
} else {
reason = new String(ra, "UTF-8");
}
}
}
onClose(code, reason);
// We have received a close, so lets set the state as early as possible.
// It seems that Handler() has a lag to deliver a message, so if the onClose
// is sent to master and just after that the other peer closes the socket,
// BufferedInputReader.read() will throw an exception which results in
// our code sending a ConnectionLost() message to master.
mState = STATE_CLOSED;
} else if (mFrameHeader.mOpcode == 9) {
// dispatch WS ping
onPing(framePayload);
} else if (mFrameHeader.mOpcode == 10) {
// dispatch WS pong
onPong(framePayload);
} else {
// should not arrive here (handled before)
throw new Exception("logic error");
}
} else {
if (!mInsideMessage) {
// new message started
mInsideMessage = true;
mMessageOpcode = mFrameHeader.mOpcode;
if (mMessageOpcode == 1 && mOptions.getValidateIncomingUtf8()) {
mUtf8Validator.reset();
}
}
if (framePayload != null) {
// immediately bail out on message too large
if (mMessagePayload.size() + framePayload.length > mOptions.getMaxMessagePayloadSize()) {
throw new WebSocketException("message payload too large");
}
// validate incoming UTF-8
if (mMessageOpcode == 1 && mOptions.getValidateIncomingUtf8() && !mUtf8Validator.validate(framePayload)) {
throw new WebSocketException("invalid UTF-8 in text message payload");
}
// buffer frame payload for message
mMessagePayload.write(framePayload);
}
// on final frame ..
if (mFrameHeader.mFin) {
if (mMessageOpcode == 1) {
// verify that UTF-8 ends on codepoint
if (mOptions.getValidateIncomingUtf8() && !mUtf8Validator.isValid()) {
throw new WebSocketException("UTF-8 text message payload ended within Unicode code point");
}
// deliver text message
if (mOptions.getReceiveTextMessagesRaw()) {
// dispatch WS text message as raw (but validated) UTF-8
onRawTextMessage(mMessagePayload.toByteArray());
} else {
// dispatch WS text message as Java String (previously already validated)
String s = new String(mMessagePayload.toByteArray(), "UTF-8");
onTextMessage(s);
}
} else if (mMessageOpcode == 2) {
// dispatch WS binary message
onBinaryMessage(mMessagePayload.toByteArray());
} else {
// should not arrive here (handled before)
throw new Exception("logic error");
}
// ok, message completed - reset all
mInsideMessage = false;
mMessagePayload.reset();
}
}
// reset frame
mFrameHeader = null;
// reprocess if more data left
return mPosition > 0;
} else {
// need more data
return false;
}
}
}
Aggregations