79 lines
1.9 KiB
Go
79 lines
1.9 KiB
Go
package fabriccontrol
|
|
|
|
import (
|
|
"encoding/binary"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
)
|
|
|
|
const (
|
|
frameMagic uint32 = 0x52415046
|
|
frameVersion uint8 = 1
|
|
frameHeaderSize = 32
|
|
maxPayload = 1024 * 1024
|
|
|
|
frameData uint8 = 5
|
|
trafficClassReliable uint8 = 4
|
|
controlForwardQUICStream uint64 = 3
|
|
)
|
|
|
|
type frame struct {
|
|
Type uint8
|
|
TrafficClass uint8
|
|
StreamID uint64
|
|
Sequence uint64
|
|
Payload []byte
|
|
}
|
|
|
|
var errInvalidFrame = errors.New("invalid fabric frame")
|
|
|
|
func readFrame(r io.Reader) (frame, error) {
|
|
header := make([]byte, frameHeaderSize)
|
|
if _, err := io.ReadFull(r, header); err != nil {
|
|
return frame{}, err
|
|
}
|
|
if binary.BigEndian.Uint32(header[0:4]) != frameMagic || header[4] != frameVersion {
|
|
return frame{}, errInvalidFrame
|
|
}
|
|
payloadLen := int(binary.BigEndian.Uint32(header[28:32]))
|
|
if payloadLen > maxPayload {
|
|
return frame{}, fmt.Errorf("%w: payload too large", errInvalidFrame)
|
|
}
|
|
out := frame{
|
|
Type: header[5],
|
|
TrafficClass: header[8],
|
|
StreamID: binary.BigEndian.Uint64(header[12:20]),
|
|
Sequence: binary.BigEndian.Uint64(header[20:28]),
|
|
}
|
|
if payloadLen > 0 {
|
|
out.Payload = make([]byte, payloadLen)
|
|
if _, err := io.ReadFull(r, out.Payload); err != nil {
|
|
return frame{}, err
|
|
}
|
|
}
|
|
return out, nil
|
|
}
|
|
|
|
func writeFrame(w io.Writer, f frame) error {
|
|
if len(f.Payload) > maxPayload {
|
|
return fmt.Errorf("%w: payload too large", errInvalidFrame)
|
|
}
|
|
header := make([]byte, frameHeaderSize)
|
|
binary.BigEndian.PutUint32(header[0:4], frameMagic)
|
|
header[4] = frameVersion
|
|
header[5] = f.Type
|
|
header[8] = f.TrafficClass
|
|
binary.BigEndian.PutUint64(header[12:20], f.StreamID)
|
|
binary.BigEndian.PutUint64(header[20:28], f.Sequence)
|
|
binary.BigEndian.PutUint32(header[28:32], uint32(len(f.Payload)))
|
|
if _, err := w.Write(header); err != nil {
|
|
return err
|
|
}
|
|
if len(f.Payload) == 0 {
|
|
return nil
|
|
}
|
|
_, err := w.Write(f.Payload)
|
|
return err
|
|
}
|