// Package protocol implements TLS-payload framing and PROXY v1/v2 parsing. package protocol import ( "encoding/binary" "spark-server/internal/pkg/errs" ) const ( Magic uint16 = 0x5350 // "SP" HeaderSize = 23 FlagCompress = 1 << 0 CmdAuth uint16 = 1 CmdHeartbeat uint16 = 2 CmdGenerateMsgID uint16 = 3 CmdSendMessage uint16 = 4 CmdSendAck uint16 = 5 CmdMessage uint16 = 6 CmdPushAck uint16 = 7 CmdReadMessage uint16 = 8 CmdRecallMessage uint16 = 9 CmdSyncInbox uint16 = 10 CmdSyncInboxFinish uint16 = 11 CmdSyncMessages uint16 = 12 CmdSyncMessagesFinish uint16 = 13 CmdKick uint16 = 14 ) // Frame is the application header after TLS. SequenceID matches request/response and PUSH_ACK only. type Frame struct { Magic uint16 Version uint8 Command uint16 Flags uint16 RequestID uint64 SequenceID uint32 Body []byte } func Encode(f Frame) ([]byte, error) { if len(f.Body) > 0xFFFFFFFF { return nil, errs.ErrFrameTooLarge } buf := make([]byte, HeaderSize+len(f.Body)) binary.BigEndian.PutUint16(buf[0:2], Magic) buf[2] = f.Version binary.BigEndian.PutUint16(buf[3:5], f.Command) binary.BigEndian.PutUint16(buf[5:7], f.Flags) binary.BigEndian.PutUint64(buf[7:15], f.RequestID) binary.BigEndian.PutUint32(buf[15:19], f.SequenceID) binary.BigEndian.PutUint32(buf[19:23], uint32(len(f.Body))) copy(buf[23:], f.Body) return buf, nil } func Decode(raw []byte, maxBody int) (Frame, error) { if len(raw) < HeaderSize { return Frame{}, errs.ErrInvalidFrame } magic := binary.BigEndian.Uint16(raw[0:2]) if magic != Magic { return Frame{}, errs.ErrInvalidFrame } bodyLen := int(binary.BigEndian.Uint32(raw[19:23])) if bodyLen > maxBody || HeaderSize+bodyLen != len(raw) { return Frame{}, errs.ErrFrameTooLarge } return Frame{ Magic: magic, Version: raw[2], Command: binary.BigEndian.Uint16(raw[3:5]), Flags: binary.BigEndian.Uint16(raw[5:7]), RequestID: binary.BigEndian.Uint64(raw[7:15]), SequenceID: binary.BigEndian.Uint32(raw[15:19]), Body: append([]byte(nil), raw[23:]...), }, nil } // LooksLikeFrame reports whether buf starts with Magic. func LooksLikeFrame(buf []byte) bool { return len(buf) >= 2 && binary.BigEndian.Uint16(buf[0:2]) == Magic } // MaybeFramePrefix is true when buf is a prefix of Magic (including a full Frame). func MaybeFramePrefix(buf []byte) bool { if len(buf) == 0 { return true } if buf[0] != byte(Magic>>8) { return false } if len(buf) == 1 { return true } return binary.BigEndian.Uint16(buf[0:2]) == Magic } // PeekBodyLength reads BodyLength from a complete header without copying the body. func PeekBodyLength(header []byte) (int, error) { if len(header) < HeaderSize { return 0, errs.ErrInvalidFrame } if binary.BigEndian.Uint16(header[0:2]) != Magic { return 0, errs.ErrInvalidFrame } return int(binary.BigEndian.Uint32(header[19:23])), nil }