All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.aoju.bus.http.socket.WebSocketProtocol Maven / Gradle / Ivy

/*
 * The MIT License
 *
 * Copyright (c) 2015-2020 aoju.org All rights reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
package org.aoju.bus.http.socket;

import org.aoju.bus.core.io.Buffer;
import org.aoju.bus.core.io.ByteString;

/**
 * web socket协议信息
 *
 * @author Kimi Liu
 * @version 5.6.5
 * @since JDK 1.8+
 */
public final class WebSocketProtocol {

    /**
     * 须附加到响应标头中的键的魔法值.
     */
    static final String ACCEPT_MAGIC = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
    /**
     * 标记这是否是消息中的最后一个片段.
     */
    static final int B0_FLAG_FIN = 0b10000000;
    /**
     * 保留标志1。必须是0.
     */
    static final int B0_FLAG_RSV1 = 0b01000000;
    /**
     * 保留标志2。必须是0.
     */
    static final int B0_FLAG_RSV2 = 0b00100000;
    /**
     * 保留标志3。必须是0.
     */
    static final int B0_FLAG_RSV3 = 0b00010000;
    /**
     * 帧操作码的字节0掩码.
     */
    static final int B0_MASK_OPCODE = 0b00001111;
    /**
     * 在操作码中标记,指示控制帧.
     */
    static final int OPCODE_FLAG_CONTROL = 0b00001000;

    /**
     * 字节1标记是否屏蔽了有效负载数据
     * 如果设置了这个标志,接下来的四个字节表示掩码键
     * 这些字节出现在{@link #B1_MASK_LENGTH}指定的任何附加字节之后.
     */
    static final int B1_FLAG_MASK = 0b10000000;
    /**
     * 有效负载长度的字节1掩码
     * 如果这个值是{@link #PAYLOAD_SHORT},接下来的两个字节表示长度
     * 如果这个值是{@link #PAYLOAD_LONG},接下来的8个字节表示长度.
     */
    static final int B1_MASK_LENGTH = 0b01111111;

    static final int OPCODE_CONTINUATION = 0x0;
    static final int OPCODE_TEXT = 0x1;
    static final int OPCODE_BINARY = 0x2;

    static final int OPCODE_CONTROL_CLOSE = 0x8;
    static final int OPCODE_CONTROL_PING = 0x9;
    static final int OPCODE_CONTROL_PONG = 0xa;

    /**
     * 帧有效载荷的最大长度。如果框架类型支持较大的有效负载,
     * 则可以使用特殊值{@link #PAYLOAD_SHORT}或{@link #PAYLOAD_LONG}.
     */
    static final long PAYLOAD_BYTE_MAX = 125L;
    /**
     * 关闭消息的最大长度(以字节为单位).
     */
    static final long CLOSE_MESSAGE_MAX = PAYLOAD_BYTE_MAX - 2;
    /**
     * 表示后面两个字节是无符号长度的{@link #B1_MASK_LENGTH}的值.
     */
    static final int PAYLOAD_SHORT = 126;
    /**
     * 帧有效载荷的最大长度表示为{@link #PAYLOAD_SHORT}.
     */
    static final long PAYLOAD_SHORT_MAX = 0xffffL;
    /**
     * {@link #B1_MASK_LENGTH}的值,该值指示接下来的八个字节是无符号长度.
     */
    static final int PAYLOAD_LONG = 127;
    /**
     * 在侦听器中抛出未检查异常时使用.
     */
    static final int CLOSE_CLIENT_GOING_AWAY = 1001;
    /**
     * 当接收到一个空的关闭帧时使用,没有状态码).
     */
    static final int CLOSE_NO_STATUS_CODE = 1005;

    private WebSocketProtocol() {
        throw new AssertionError("No instances.");
    }

    static void toggleMask(Buffer.UnsafeCursor cursor, byte[] key) {
        int keyIndex = 0;
        int keyLength = key.length;
        do {
            byte[] buffer = cursor.data;
            for (int i = cursor.start, end = cursor.end; i < end; i++, keyIndex++) {
                keyIndex %= keyLength;
                buffer[i] = (byte) (buffer[i] ^ key[keyIndex]);
            }
        } while (cursor.next() != -1);
    }

    static String closeCodeExceptionMessage(int code) {
        if (code < 1000 || code >= 5000) {
            return "Code must be in range [1000,5000): " + code;
        } else if ((code >= 1004 && code <= 1006) || (code >= 1012 && code <= 2999)) {
            return "Code " + code + " is reserved and may not be used.";
        } else {
            return null;
        }
    }

    static void validateCloseCode(int code) {
        String message = closeCodeExceptionMessage(code);
        if (message != null) throw new IllegalArgumentException(message);
    }

    public static String acceptHeader(String key) {
        return ByteString.encodeUtf8(key + WebSocketProtocol.ACCEPT_MAGIC).sha1().base64();
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy