-
Notifications
You must be signed in to change notification settings - Fork 1
/
serializer.go
120 lines (101 loc) · 2.39 KB
/
serializer.go
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
107
108
109
110
111
112
113
114
115
116
117
118
119
120
package stomp
import (
"fmt"
"sort"
"strings"
)
const (
nullOctet = byte('\x00')
lineFeed = byte('\n')
)
func serialize(f *Frame) []byte {
sb := &strings.Builder{}
// command
sb.WriteString(fmt.Sprintf("%s\n", f.command))
// headers
if f.headers != nil {
for _, hk := range f.getSortedHeaderKeys() {
sb.WriteString(
fmt.Sprintf("%s:%s\n", escape(hk), escape(f.headers[Header(hk)])),
)
}
}
// Indicator of end of headers
sb.WriteString("\n")
// body
if f.body != nil {
sb.Write(f.body)
}
// NUL
sb.Write([]byte{nullOctet})
return []byte(sb.String())
}
func (f *Frame) getSortedHeaderKeys() []string {
keys := make([]string, 0, len(f.headers))
for hk := range f.headers {
keys = append(keys, string(hk))
}
sort.Strings(keys)
return keys
}
func deserialize(buf []byte, f *Frame) error {
// The frame must end at NUL but can have newlines following that.
nulPos := 0
for i := len(buf) - 1; i >= 0; i-- {
if buf[i] == lineFeed {
continue
}
if buf[i] == nullOctet {
nulPos = i
break
}
return errorMsg(errByteFormat, "Message frame must end with NUL byte")
}
// Truncate buf to exclude the NUL.
// If this is skipped the newlines and the NUL will end up in the body.
buf = buf[:nulPos]
// Remove all carriage-returns
s := strings.ReplaceAll(string(buf), "\r", "") // Ignore "\r" chars
lines := strings.Split(s, "\n") // Split at newline\\
cmd := Command(lines[0])
lines = lines[1:]
// Extracting the headers & body
headers := make(map[Header]string)
for _, l := range lines {
if l == "" {
break
}
h := strings.Split(l, ":")
if len(h) != 2 {
return errorMsg(errByteFormat, "Header must contain only one ':', bad header: "+l)
}
k, v := Header(unescape(h[0])), unescape(h[1])
headers[k] = v
}
// Fetch body
i := strings.Index(s, "\n\n")
if i == -1 {
return errorMsg(errByteFormat, "End of headers must be marked by two newlines: \\n\\n")
}
var body []byte = nil
if i+2 != len(buf) {
body = []byte(s[i+2 : len(buf)])
}
// Fill
f.command = cmd
f.headers = headers
f.body = body
return nil
}
func unescape(l string) string {
l = strings.ReplaceAll(l, `\\`, "\\")
l = strings.ReplaceAll(l, `\n`, "\n")
l = strings.ReplaceAll(l, `\c`, ":")
return l
}
func escape(l string) string {
l = strings.ReplaceAll(l, "\\", `\\`)
l = strings.ReplaceAll(l, "\n", `\n`)
l = strings.ReplaceAll(l, ":", `\c`)
return l
}