解析原始 websocket 框架很容易。但是您必须一次检查一个字节的标头。
这是一个粗略的例子:
我留下了一些 TODO 供您自己解决(当然是在阅读 RFC-6455 规范之后)
您可以验证的事情:
基本帧协议:RFC-6455 - 第 5.2 节
- 是否找到了规范中定义的有效操作码之一?
- RSV 位是否使用不当?
客户端到服务器屏蔽:RFC 6455 - 第 5.3 节
- 如果帧是由客户端发送的,帧是否被屏蔽?
- 掩码是从帧到帧随机的吗?
- 不允许 [0x00, 0x00, 0x00, 0x00] 作为掩码。
分段:RFC 6455 - 第 5.4 节
- 它是一个零散的控制框架吗?
- 由多个帧组成的大消息的碎片是否乱序?
- 一条新消息在前一条消息完成之前是否已开始并带有 FIN 标志?
控制帧:RFC 6455 - 第 5.5 节
- 控制帧的有效载荷长度是否超过 125 字节?
- 有效载荷是否碎片化?
关闭帧:RFC 6455 - 第 5.5.1 节
- 如果有效载荷中提供了状态代码,该状态代码是否符合第 7.4.1 节中声明的状态代码之一?不要忘记检查在 RFC 最终确定后添加的 websocket 状态代码的 IANA 注册表)
- 状态码是否允许在帧中通过网络发送?(例如,参见代码 1005 和 1006)
- 如果框架中提供了 /reason/,它是否符合 UTF-8 编码规则?
- 在关闭帧之后,您是否收到任何类型的帧?(这是一个禁忌)
数据帧:RFC 6455 - 第 5.6 节
- 如果您收到 TEXT 有效负载数据(来自 TEXT + CONTINUATION 帧),有效负载数据是否符合 UTF-8 编码规则?
虽然您可以在单个帧级别进行验证,但您会发现上面的一些验证是对多个帧之间的状态和行为的验证。您可以在发送和接收数据中找到更多此类验证:RFC 6455 - 第 6 节。
但是,如果您在混合中有扩展,那么您还需要从协商的扩展堆栈的角度处理帧。使用扩展时,上述某些测试似乎无效。
示例:您有Compression Extension (RFC-7692)(例如permessage-deflate
)正在使用,则无法通过网络外的原始帧验证 TEXT 有效负载,因为您必须首先通过扩展传递帧。请注意,扩展可以更改碎片以满足其需求,这也可能会扰乱您的验证。
package websocket;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
public class RawParse
{
public static class Frame
{
byte opcode;
boolean fin;
byte payload[];
}
public static Frame parse(byte raw[])
{
// easier to do this via ByteBuffer
ByteBuffer buf = ByteBuffer.wrap(raw);
// Fin + RSV + OpCode byte
Frame frame = new Frame();
byte b = buf.get();
frame.fin = ((b & 0x80) != 0);
boolean rsv1 = ((b & 0x40) != 0);
boolean rsv2 = ((b & 0x20) != 0);
boolean rsv3 = ((b & 0x10) != 0);
frame.opcode = (byte)(b & 0x0F);
// TODO: add control frame fin validation here
// TODO: add frame RSV validation here
// Masked + Payload Length
b = buf.get();
boolean masked = ((b & 0x80) != 0);
int payloadLength = (byte)(0x7F & b);
int byteCount = 0;
if (payloadLength == 0x7F)
{
// 8 byte extended payload length
byteCount = 8;
}
else if (payloadLength == 0x7E)
{
// 2 bytes extended payload length
byteCount = 2;
}
// Decode Payload Length
while (--byteCount > 0)
{
b = buf.get();
payloadLength |= (b & 0xFF) << (8 * byteCount);
}
// TODO: add control frame payload length validation here
byte maskingKey[] = null;
if (masked)
{
// Masking Key
maskingKey = new byte[4];
buf.get(maskingKey,0,4);
}
// TODO: add masked + maskingkey validation here
// Payload itself
frame.payload = new byte[payloadLength];
buf.get(frame.payload,0,payloadLength);
// Demask (if needed)
if (masked)
{
for (int i = 0; i < frame.payload.length; i++)
{
frame.payload[i] ^= maskingKey[i % 4];
}
}
return frame;
}
public static void main(String[] args)
{
Charset UTF8 = Charset.forName("UTF-8");
Frame closeFrame = parse(hexToByteArray("8800"));
System.out.printf("closeFrame.opcode = %d%n",closeFrame.opcode);
System.out.printf("closeFrame.payload.length = %d%n",closeFrame.payload.length);
// Examples from https://www.rfc-editor.org/rfc/rfc6455#section-5.7
Frame unmaskedTextFrame = parse(hexToByteArray("810548656c6c6f"));
System.out.printf("unmaskedTextFrame.opcode = %d%n",unmaskedTextFrame.opcode);
System.out.printf("unmaskedTextFrame.payload.length = %d%n",unmaskedTextFrame.payload.length);
System.out.printf("unmaskedTextFrame.payload = \"%s\"%n",new String(unmaskedTextFrame.payload,UTF8));
Frame maskedTextFrame = parse(hexToByteArray("818537fa213d7f9f4d5158"));
System.out.printf("maskedTextFrame.opcode = %d%n",maskedTextFrame.opcode);
System.out.printf("maskedTextFrame.payload.length = %d%n",maskedTextFrame.payload.length);
System.out.printf("maskedTextFrame.payload = \"%s\"%n",new String(maskedTextFrame.payload,UTF8));
}
public static byte[] hexToByteArray(String hstr)
{
if ((hstr.length() < 0) || ((hstr.length() % 2) != 0))
{
throw new IllegalArgumentException(String.format("Invalid string length of <%d>",hstr.length()));
}
int size = hstr.length() / 2;
byte buf[] = new byte[size];
byte hex;
int len = hstr.length();
int idx = (int)Math.floor(((size * 2) - (double)len) / 2);
for (int i = 0; i < len; i++)
{
hex = 0;
if (i >= 0)
{
hex = (byte)(Character.digit(hstr.charAt(i),16) << 4);
}
i++;
hex += (byte)(Character.digit(hstr.charAt(i),16));
buf[idx] = hex;
idx++;
}
return buf;
}
}