Search in sources :

Example 1 with Utf8Validator

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;
        }
    }
}
Also used : WebSocketException(io.crossbar.autobahn.websocket.exceptions.WebSocketException) Utf8Validator(io.crossbar.autobahn.websocket.utils.Utf8Validator) SocketException(java.net.SocketException) WebSocketException(io.crossbar.autobahn.websocket.exceptions.WebSocketException) IOException(java.io.IOException) UnsupportedEncodingException(java.io.UnsupportedEncodingException)

Aggregations

WebSocketException (io.crossbar.autobahn.websocket.exceptions.WebSocketException)1 Utf8Validator (io.crossbar.autobahn.websocket.utils.Utf8Validator)1 IOException (java.io.IOException)1 UnsupportedEncodingException (java.io.UnsupportedEncodingException)1 SocketException (java.net.SocketException)1