Skip to content

Commit 91cfd78

Browse files
authored
fix(realtime): account for null refs when encoding messages (#1862)
1 parent a66387e commit 91cfd78

File tree

2 files changed

+114
-14
lines changed

2 files changed

+114
-14
lines changed

packages/core/realtime-js/src/lib/serializer.ts

Lines changed: 21 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33
import { CHANNEL_EVENTS } from '../lib/constants'
44

55
export type Msg<T> = {
6-
join_ref: string
7-
ref: string
6+
join_ref?: string | null
7+
ref?: string | null
88
topic: string
99
event: string
1010
payload: T
@@ -42,19 +42,22 @@ export default class Serializer {
4242
}
4343

4444
private _binaryEncodePush(message: Msg<ArrayBuffer>) {
45-
const { join_ref, ref, event, topic, payload } = message
46-
const metaLength = this.META_LENGTH + join_ref.length + ref.length + topic.length + event.length
45+
const { event, topic, payload } = message
46+
const ref = message.ref ?? ''
47+
const joinRef = message.join_ref ?? ''
48+
49+
const metaLength = this.META_LENGTH + joinRef.length + ref.length + topic.length + event.length
4750

4851
const header = new ArrayBuffer(this.HEADER_LENGTH + metaLength)
4952
let view = new DataView(header)
5053
let offset = 0
5154

5255
view.setUint8(offset++, this.KINDS.push) // kind
53-
view.setUint8(offset++, join_ref.length)
56+
view.setUint8(offset++, joinRef.length)
5457
view.setUint8(offset++, ref.length)
5558
view.setUint8(offset++, topic.length)
5659
view.setUint8(offset++, event.length)
57-
Array.from(join_ref, (char) => view.setUint8(offset++, char.charCodeAt(0)))
60+
Array.from(joinRef, (char) => view.setUint8(offset++, char.charCodeAt(0)))
5861
Array.from(ref, (char) => view.setUint8(offset++, char.charCodeAt(0)))
5962
Array.from(topic, (char) => view.setUint8(offset++, char.charCodeAt(0)))
6063
Array.from(event, (char) => view.setUint8(offset++, char.charCodeAt(0)))
@@ -75,13 +78,15 @@ export default class Serializer {
7578
}
7679

7780
private _encodeBinaryUserBroadcastPush(message: Msg<{ event: string } & { [key: string]: any }>) {
78-
const { join_ref, ref, topic } = message
81+
const topic = message.topic
82+
const ref = message.ref ?? ''
83+
const joinRef = message.join_ref ?? ''
7984
const userEvent = message.payload.event
8085
const userPayload = message.payload?.payload ?? new ArrayBuffer(0)
8186

8287
const metaLength =
8388
this.USER_BROADCAST_PUSH_META_LENGTH +
84-
join_ref.length +
89+
joinRef.length +
8590
ref.length +
8691
topic.length +
8792
userEvent.length
@@ -91,12 +96,12 @@ export default class Serializer {
9196
let offset = 0
9297

9398
view.setUint8(offset++, this.KINDS.userBroadcastPush) // kind
94-
view.setUint8(offset++, join_ref.length)
99+
view.setUint8(offset++, joinRef.length)
95100
view.setUint8(offset++, ref.length)
96101
view.setUint8(offset++, topic.length)
97102
view.setUint8(offset++, userEvent.length)
98103
view.setUint8(offset++, this.BINARY_ENCODING)
99-
Array.from(join_ref, (char) => view.setUint8(offset++, char.charCodeAt(0)))
104+
Array.from(joinRef, (char) => view.setUint8(offset++, char.charCodeAt(0)))
100105
Array.from(ref, (char) => view.setUint8(offset++, char.charCodeAt(0)))
101106
Array.from(topic, (char) => view.setUint8(offset++, char.charCodeAt(0)))
102107
Array.from(userEvent, (char) => view.setUint8(offset++, char.charCodeAt(0)))
@@ -109,7 +114,9 @@ export default class Serializer {
109114
}
110115

111116
private _encodeJsonUserBroadcastPush(message: Msg<{ event: string } & { [key: string]: any }>) {
112-
const { join_ref, ref, topic } = message
117+
const topic = message.topic
118+
const ref = message.ref ?? ''
119+
const joinRef = message.join_ref ?? ''
113120
const userEvent = message.payload.event
114121
const userPayload = message.payload?.payload ?? {}
115122

@@ -118,7 +125,7 @@ export default class Serializer {
118125

119126
const metaLength =
120127
this.USER_BROADCAST_PUSH_META_LENGTH +
121-
join_ref.length +
128+
joinRef.length +
122129
ref.length +
123130
topic.length +
124131
userEvent.length
@@ -128,12 +135,12 @@ export default class Serializer {
128135
let offset = 0
129136

130137
view.setUint8(offset++, this.KINDS.userBroadcastPush) // kind
131-
view.setUint8(offset++, join_ref.length)
138+
view.setUint8(offset++, joinRef.length)
132139
view.setUint8(offset++, ref.length)
133140
view.setUint8(offset++, topic.length)
134141
view.setUint8(offset++, userEvent.length)
135142
view.setUint8(offset++, this.JSON_ENCODING)
136-
Array.from(join_ref, (char) => view.setUint8(offset++, char.charCodeAt(0)))
143+
Array.from(joinRef, (char) => view.setUint8(offset++, char.charCodeAt(0)))
137144
Array.from(ref, (char) => view.setUint8(offset++, char.charCodeAt(0)))
138145
Array.from(topic, (char) => view.setUint8(offset++, char.charCodeAt(0)))
139146
Array.from(userEvent, (char) => view.setUint8(offset++, char.charCodeAt(0)))

packages/core/realtime-js/test/serializer.test.ts

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,13 @@ const decodeAsync = (
2828
}
2929

3030
let exampleMsg = { join_ref: '0', ref: '1', topic: 't', event: 'e', payload: { foo: 1 } }
31+
let missingRefExampleMsg = {
32+
join_ref: null,
33+
ref: null,
34+
topic: 't',
35+
event: 'e',
36+
payload: { foo: 1 },
37+
}
3138

3239
// \x01\x04
3340
let binPayload = () => {
@@ -43,10 +50,20 @@ describe('JSON', () => {
4350
expect(result).toBe('["0","1","t","e",{"foo":1}]')
4451
})
4552

53+
it('encodes missing refs', async () => {
54+
const result = await encodeAsync(serializer, missingRefExampleMsg)
55+
expect(result).toBe('[null,null,"t","e",{"foo":1}]')
56+
})
57+
4658
it('decodes', async () => {
4759
const result = await decodeAsync(serializer, '["0","1","t","e",{"foo":1}]')
4860
expect(result).toEqual(exampleMsg)
4961
})
62+
63+
it('decodes missing refs', async () => {
64+
const result = await decodeAsync(serializer, '[null,null,"t","e",{"foo":1}]')
65+
expect(result).toEqual(missingRefExampleMsg)
66+
})
5067
})
5168

5269
describe('binary', () => {
@@ -63,6 +80,30 @@ describe('binary', () => {
6380
expect(decoder.decode(result as ArrayBuffer)).toBe(bin)
6481
})
6582

83+
it('encodes push with undefined join_ref and ref', async () => {
84+
let buffer = binPayload()
85+
let bin = '\0\x00\x00\x01\x01te\x01\x04'
86+
const result = await encodeAsync(serializer, {
87+
join_ref: undefined,
88+
ref: undefined,
89+
topic: 't',
90+
event: 'e',
91+
payload: buffer,
92+
})
93+
expect(decoder.decode(result as ArrayBuffer)).toBe(bin)
94+
})
95+
96+
it('encodes push with no join_ref no ref', async () => {
97+
let buffer = binPayload()
98+
let bin = '\0\x00\x00\x01\x01te\x01\x04'
99+
const result = await encodeAsync(serializer, {
100+
topic: 't',
101+
event: 'e',
102+
payload: buffer,
103+
})
104+
expect(decoder.decode(result as ArrayBuffer)).toBe(bin)
105+
})
106+
66107
it('encodes variable length segments', async () => {
67108
let buffer = binPayload()
68109
let bin = '\0\x02\x01\x03\x02101topev\x01\x04'
@@ -106,6 +147,33 @@ describe('binary', () => {
106147
expect(decoder.decode(result as ArrayBuffer)).toBe(bin)
107148
})
108149

150+
it('encodes user broadcast push with JSON payload no refs', async () => {
151+
// 3 -> user_broadcast_push
152+
// 0 join_ref length
153+
// 0 for ref length
154+
// 3 for topic length
155+
// 10 for user event length
156+
// 1 for JSON encoding
157+
// actual join ref
158+
// actual ref
159+
// actual topic
160+
// actual user event
161+
// actual payload
162+
let bin = '\x03\x00\x00\x03\x0a\x01topuser-event{"a":"b"}'
163+
164+
const result = await encodeAsync(serializer, {
165+
topic: 'top',
166+
event: 'broadcast',
167+
payload: {
168+
event: 'user-event',
169+
payload: {
170+
a: 'b',
171+
},
172+
},
173+
})
174+
expect(decoder.decode(result as ArrayBuffer)).toBe(bin)
175+
})
176+
109177
it('encodes user broadcast push with Binary payload', async () => {
110178
// 3 -> user_broadcast_push
111179
// 2 join_ref length
@@ -133,6 +201,31 @@ describe('binary', () => {
133201
expect(decoder.decode(result as ArrayBuffer)).toBe(bin)
134202
})
135203

204+
it('encodes user broadcast push with Binary payload no refs', async () => {
205+
// 3 -> user_broadcast_push
206+
// 0 join_ref length
207+
// 0 for ref length
208+
// 3 for topic length
209+
// 10 for user event length
210+
// 0 for Binary encoding
211+
// actual join ref
212+
// actual ref
213+
// actual topic
214+
// actual user event
215+
// actual payload
216+
let bin = '\x03\x00\x00\x03\x0a\x00topuser-event\x01\x04'
217+
218+
const result = await encodeAsync(serializer, {
219+
topic: 'top',
220+
event: 'broadcast',
221+
payload: {
222+
event: 'user-event',
223+
payload: binPayload(),
224+
},
225+
})
226+
expect(decoder.decode(result as ArrayBuffer)).toBe(bin)
227+
})
228+
136229
it('decodes push payload as JSON', async () => {
137230
let bin = '\0\x03\x03\n123topsome-event{"a":"b"}'
138231
let buffer = new TextEncoder().encode(bin).buffer

0 commit comments

Comments
 (0)