Files
huya-go/internal/huya/wss_message.go
T
2026-07-06 18:08:23 +08:00

114 lines
2.8 KiB
Go

package huya
import (
"encoding/binary"
"fmt"
"math/rand"
)
const (
WSSCommandRPCRequest byte = 0x03
WSSCommandRPCResponse byte = 0x04
WSSCommandAuth byte = 0x0a
WSSCommandHeartbeatSend byte = 0x10
WSSCommandHeartbeatRecv byte = 0x11
WSSCommandConfirmSend byte = 0x17
WSSCommandRegister byte = 0x21
wssSeqHeartbeat uint32 = 0x1d00000d
wssSeqWSLaunch uint32 = 0x1d000078
wssSeqGetConfig uint32 = 0x1d000076
wssSeqRegister uint32 = 0x1d00003c
wssSeqConfirm uint32 = 0x1d000002
wssSeqBusiness uint32 = 0x1d000106
)
var (
wssTailBytes = []byte{0x2c, 0x36, 0x00, 0x4c, 0x5c, 0x66, 0x00}
wssBigTailSuffix = []byte{0x4c, 0x5c, 0x66, 0x00}
)
// WSSMessage 是虎牙 WebSocketCommand 的 Go 表达。
//
// 外层格式来自浏览器/参考项目:
//
// 1B version(0) + 1B command + 4B sequence + body
type WSSMessage struct {
Command byte
Sequence uint32
Body []byte
}
func (m WSSMessage) Encode() []byte {
out := make([]byte, 6+len(m.Body))
out[0] = 0
out[1] = m.Command
binary.BigEndian.PutUint32(out[2:6], m.Sequence)
copy(out[6:], m.Body)
return out
}
func DecodeWSSMessage(data []byte) (WSSMessage, error) {
if len(data) < 6 {
return WSSMessage{}, fmt.Errorf("wss: 消息太短: %d", len(data))
}
return WSSMessage{
Command: data[1],
Sequence: binary.BigEndian.Uint32(data[2:6]),
Body: append([]byte(nil), data[6:]...),
}, nil
}
func encodeWSSRPCBody(wupData []byte) []byte {
if len(wupData) < 4 {
out := append([]byte{}, wupData...)
return append(out, wssTailBytes...)
}
declaredLen := int(binary.BigEndian.Uint32(wupData[:4]))
wupBody := wupData
if declaredLen == len(wupData) {
wupBody = wupData[4:]
}
if len(wupBody) <= 0xff {
out := append([]byte{}, wupData...)
return append(out, wssTailBytes...)
}
trace := NewWSSBigTailTrace()
packetLen := len(wupData)
out := make([]byte, 0, 1+4+len(wupBody)+len(trace))
out = append(out, byte(packetLen&0xff))
var lenBuf [4]byte
binary.BigEndian.PutUint32(lenBuf[:], uint32(packetLen))
out = append(out, lenBuf[:]...)
out = append(out, wupBody...)
out = append(out, trace...)
return out
}
func extractWUPFromWSSBody(body []byte) []byte {
if len(body) < 5 {
return body
}
// 大包格式: [1B prefix][4B wup_len][裸 WUP body][tail]
wupLen := int(binary.BigEndian.Uint32(body[1:5]))
if 5+wupLen <= len(body) && len(body) >= 7 && body[5] == 0x10 && body[6] == 0x03 {
return body[5 : 5+wupLen]
}
// 小包格式: [4B total_len][WUP body][tail]
totalLen := int(binary.BigEndian.Uint32(body[:4]))
if 8 <= totalLen && totalLen <= len(body) && body[4] == 0x10 && body[5] == 0x03 {
return body[:totalLen]
}
if 5+wupLen <= len(body) {
return body[5 : 5+wupLen]
}
return body
}
func NewWSSBigTailTrace() []byte {
trace := lowerHex16(rand.Uint64())
out := []byte(",6%" + trace + ":" + trace + ":0:3")
return append(out, wssBigTailSuffix...)
}