summaryrefslogtreecommitdiff
path: root/internal/packet/framer.go
blob: 1a76ea6113cdb488bd00421588abc7916e23cf9e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
package packet

import (
	"context"
	"encoding/binary"
	"errors"
	"fmt"
	"io"

	"github.com/kyren223/eko/pkg/assert"
	"github.com/kyren223/eko/pkg/util"
)

const framerPacketCapacity = 10

var (
	PacketUnsupportedVersion  error = errors.New("packet error: unsupported version")
	PacketUnsupportedEncoding error = errors.New("packet error: unsupported encoding")
	PacketUnsupportedType     error = errors.New("packet error: unsupported type")
)

type packetFramer struct {
	buffer []byte
	len    uint16
	in     chan<- Packet
	inErr  chan<- error
}

func RunFramer(ctx context.Context, reader io.Reader) (out <-chan Packet, outErr <-chan error) {
	ch := make(chan Packet, framerPacketCapacity)
	errCh := make(chan error)

	framer := packetFramer{
		buffer: make([]byte, PACKET_MAX_SIZE),
		len:    0,
		in:     ch,
		inErr:  errCh,
	}

	go framer.run(ctx, util.NewChannelReader(ctx, reader))

	return ch, errCh
}

func (f *packetFramer) run(ctx context.Context, reader util.ChannelReader) {
	defer close(f.in)
	defer close(f.inErr)

outer:
	for {
		select {
		case data := <-reader.Out:
			dataRead := 0
			for dataRead < len(data) {
				n := copy(f.buffer[f.len:], data[dataRead:])
				assert.Assert(0 <= n && n <= int(PACKET_MAX_SIZE), "n must fit in a u16")
				f.len += uint16(n)
				dataRead += n
				if err := f.parse(); err != nil {
					f.inErr <- err
					break outer
				}
			}

		case err := <-reader.Err:
			f.inErr <- err
			break outer

		case <-ctx.Done():
			f.inErr <- ctx.Err()
			break outer
		}
	}
}

func (f *packetFramer) parse() error {
	for f.len > HEADER_SIZE {
		if f.buffer[VERSION_OFFSET] != VERSION {
			return fmt.Errorf("%w version=%v", PacketUnsupportedVersion, f.buffer[VERSION_OFFSET])
		}

		encoding := Encoding(f.buffer[ENCODING_OFFSET] >> 6)
		packetType := PacketType(f.buffer[TYPE_OFFSET] & 63)
		if !encoding.IsSupported() {
			return PacketUnsupportedEncoding
		}
		if !packetType.IsSupported() {
			return PacketUnsupportedType
		}

		length := binary.BigEndian.Uint16(f.buffer[LENGTH_OFFSET:])
		if f.len-HEADER_SIZE < length {
			// Wait for more data to arrive
			return nil
		}

		fullLength := HEADER_SIZE + length
		packetBuffer := make([]byte, fullLength)
		copy(packetBuffer, f.buffer[:fullLength])

		f.len = uint16(copy(f.buffer, f.buffer[fullLength:f.len]))

		f.in <- Packet{packetBuffer}
	}
	return nil
}