Skip to content

Commit f5f31b0

Browse files
authored
fix(realtime): simplify serializer by removing unnecessary types of messages (#1871)
1 parent c16602c commit f5f31b0

File tree

2 files changed

+6
-232
lines changed

2 files changed

+6
-232
lines changed

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

Lines changed: 6 additions & 138 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
// This file draws heavily from https://github.com/phoenixframework/phoenix/commit/cf098e9cf7a44ee6479d31d911a97d3c7430c6fe
22
// License: https://github.com/phoenixframework/phoenix/blob/master/LICENSE.md
3-
import { CHANNEL_EVENTS } from '../lib/constants'
4-
53
export type Msg<T> = {
64
join_ref?: string | null
75
ref?: string | null
@@ -14,21 +12,14 @@ export default class Serializer {
1412
HEADER_LENGTH = 1
1513
META_LENGTH = 4
1614
USER_BROADCAST_PUSH_META_LENGTH = 5
17-
KINDS = { push: 0, reply: 1, broadcast: 2, userBroadcastPush: 3, userBroadcast: 4 }
15+
KINDS = { userBroadcastPush: 3, userBroadcast: 4 }
1816
BINARY_ENCODING = 0
1917
JSON_ENCODING = 1
20-
BROADCAST = 'broadcast'
21-
22-
encode(
23-
msg: Msg<{ [key: string]: any } | ArrayBuffer>,
24-
callback: (result: ArrayBuffer | string) => any
25-
) {
26-
if (this._isArrayBuffer(msg.payload)) {
27-
return callback(this._binaryEncodePush(msg as Msg<ArrayBuffer>))
28-
}
18+
BROADCAST_EVENT = 'broadcast'
2919

20+
encode(msg: Msg<{ [key: string]: any }>, callback: (result: ArrayBuffer | string) => any) {
3021
if (
31-
msg.event === this.BROADCAST &&
22+
msg.event === this.BROADCAST_EVENT &&
3223
!(msg.payload instanceof ArrayBuffer) &&
3324
typeof msg.payload.event === 'string'
3425
) {
@@ -41,34 +32,6 @@ export default class Serializer {
4132
return callback(JSON.stringify(payload))
4233
}
4334

44-
private _binaryEncodePush(message: Msg<ArrayBuffer>) {
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
50-
51-
const header = new ArrayBuffer(this.HEADER_LENGTH + metaLength)
52-
let view = new DataView(header)
53-
let offset = 0
54-
55-
view.setUint8(offset++, this.KINDS.push) // kind
56-
view.setUint8(offset++, joinRef.length)
57-
view.setUint8(offset++, ref.length)
58-
view.setUint8(offset++, topic.length)
59-
view.setUint8(offset++, event.length)
60-
Array.from(joinRef, (char) => view.setUint8(offset++, char.charCodeAt(0)))
61-
Array.from(ref, (char) => view.setUint8(offset++, char.charCodeAt(0)))
62-
Array.from(topic, (char) => view.setUint8(offset++, char.charCodeAt(0)))
63-
Array.from(event, (char) => view.setUint8(offset++, char.charCodeAt(0)))
64-
65-
var combined = new Uint8Array(header.byteLength + payload.byteLength)
66-
combined.set(new Uint8Array(header), 0)
67-
combined.set(new Uint8Array(payload), header.byteLength)
68-
69-
return combined.buffer
70-
}
71-
7235
private _binaryEncodeUserBroadcastPush(message: Msg<{ event: string } & { [key: string]: any }>) {
7336
if (this._isArrayBuffer(message.payload?.payload)) {
7437
return this._encodeBinaryUserBroadcastPush(message)
@@ -172,106 +135,11 @@ export default class Serializer {
172135
const kind = view.getUint8(0)
173136
const decoder = new TextDecoder()
174137
switch (kind) {
175-
case this.KINDS.push:
176-
return this._decodePush(buffer, view, decoder)
177-
case this.KINDS.reply:
178-
return this._decodeReply(buffer, view, decoder)
179-
case this.KINDS.broadcast:
180-
return this._decodeBroadcast(buffer, view, decoder)
181138
case this.KINDS.userBroadcast:
182139
return this._decodeUserBroadcast(buffer, view, decoder)
183140
}
184141
}
185142

186-
private _decodePush(
187-
buffer: ArrayBuffer,
188-
view: DataView,
189-
decoder: TextDecoder
190-
): {
191-
join_ref: string
192-
ref: null
193-
topic: string
194-
event: string
195-
payload: { [key: string]: any }
196-
} {
197-
const joinRefSize = view.getUint8(1)
198-
const topicSize = view.getUint8(2)
199-
const eventSize = view.getUint8(3)
200-
let offset = this.HEADER_LENGTH + this.META_LENGTH - 1 // pushes have no ref
201-
const joinRef = decoder.decode(buffer.slice(offset, offset + joinRefSize))
202-
offset = offset + joinRefSize
203-
const topic = decoder.decode(buffer.slice(offset, offset + topicSize))
204-
offset = offset + topicSize
205-
const event = decoder.decode(buffer.slice(offset, offset + eventSize))
206-
offset = offset + eventSize
207-
const data = JSON.parse(decoder.decode(buffer.slice(offset, buffer.byteLength)))
208-
return {
209-
join_ref: joinRef,
210-
ref: null,
211-
topic: topic,
212-
event: event,
213-
payload: data,
214-
}
215-
}
216-
217-
private _decodeReply(
218-
buffer: ArrayBuffer,
219-
view: DataView,
220-
decoder: TextDecoder
221-
): {
222-
join_ref: string
223-
ref: string
224-
topic: string
225-
event: CHANNEL_EVENTS.reply
226-
payload: { status: string; response: { [key: string]: any } }
227-
} {
228-
const joinRefSize = view.getUint8(1)
229-
const refSize = view.getUint8(2)
230-
const topicSize = view.getUint8(3)
231-
const eventSize = view.getUint8(4)
232-
let offset = this.HEADER_LENGTH + this.META_LENGTH
233-
const joinRef = decoder.decode(buffer.slice(offset, offset + joinRefSize))
234-
offset = offset + joinRefSize
235-
const ref = decoder.decode(buffer.slice(offset, offset + refSize))
236-
offset = offset + refSize
237-
const topic = decoder.decode(buffer.slice(offset, offset + topicSize))
238-
offset = offset + topicSize
239-
const event = decoder.decode(buffer.slice(offset, offset + eventSize))
240-
offset = offset + eventSize
241-
const data = JSON.parse(decoder.decode(buffer.slice(offset, buffer.byteLength)))
242-
const payload = { status: event, response: data }
243-
return {
244-
join_ref: joinRef,
245-
ref: ref,
246-
topic: topic,
247-
event: CHANNEL_EVENTS.reply,
248-
payload: payload,
249-
}
250-
}
251-
252-
private _decodeBroadcast(
253-
buffer: ArrayBuffer,
254-
view: DataView,
255-
decoder: TextDecoder
256-
): {
257-
join_ref: null
258-
ref: null
259-
topic: string
260-
event: string
261-
payload: { [key: string]: any }
262-
} {
263-
const topicSize = view.getUint8(1)
264-
const eventSize = view.getUint8(2)
265-
let offset = this.HEADER_LENGTH + 2
266-
const topic = decoder.decode(buffer.slice(offset, offset + topicSize))
267-
offset = offset + topicSize
268-
const event = decoder.decode(buffer.slice(offset, offset + eventSize))
269-
offset = offset + eventSize
270-
const data = JSON.parse(decoder.decode(buffer.slice(offset, buffer.byteLength)))
271-
272-
return { join_ref: null, ref: null, topic: topic, event: event, payload: data }
273-
}
274-
275143
private _decodeUserBroadcast(
276144
buffer: ArrayBuffer,
277145
view: DataView,
@@ -301,7 +169,7 @@ export default class Serializer {
301169
payloadEncoding === this.JSON_ENCODING ? JSON.parse(decoder.decode(payload)) : payload
302170

303171
const data: { [key: string]: any } = {
304-
type: this.BROADCAST,
172+
type: this.BROADCAST_EVENT,
305173
event: userEvent,
306174
payload: parsedPayload,
307175
}
@@ -311,7 +179,7 @@ export default class Serializer {
311179
data['meta'] = JSON.parse(metadata)
312180
}
313181

314-
return { join_ref: null, ref: null, topic: topic, event: this.BROADCAST, payload: data }
182+
return { join_ref: null, ref: null, topic: topic, event: this.BROADCAST_EVENT, payload: data }
315183
}
316184

317185
private _isArrayBuffer(buffer: any): boolean {

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

Lines changed: 0 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -67,57 +67,6 @@ describe('JSON', () => {
6767
})
6868

6969
describe('binary', () => {
70-
it('encodes push', async () => {
71-
let buffer = binPayload()
72-
let bin = '\0\x01\x01\x01\x0101te\x01\x04'
73-
const result = await encodeAsync(serializer, {
74-
join_ref: '0',
75-
ref: '1',
76-
topic: 't',
77-
event: 'e',
78-
payload: buffer,
79-
})
80-
expect(decoder.decode(result as ArrayBuffer)).toBe(bin)
81-
})
82-
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-
107-
it('encodes variable length segments', async () => {
108-
let buffer = binPayload()
109-
let bin = '\0\x02\x01\x03\x02101topev\x01\x04'
110-
111-
const result = await encodeAsync(serializer, {
112-
join_ref: '10',
113-
ref: '1',
114-
topic: 'top',
115-
event: 'ev',
116-
payload: buffer,
117-
})
118-
expect(decoder.decode(result as ArrayBuffer)).toBe(bin)
119-
})
120-
12170
it('encodes user broadcast push with JSON payload', async () => {
12271
// 3 -> user_broadcast_push
12372
// 2 join_ref length
@@ -226,49 +175,6 @@ describe('binary', () => {
226175
expect(decoder.decode(result as ArrayBuffer)).toBe(bin)
227176
})
228177

229-
it('decodes push payload as JSON', async () => {
230-
let bin = '\0\x03\x03\n123topsome-event{"a":"b"}'
231-
let buffer = new TextEncoder().encode(bin).buffer
232-
233-
const result = await decodeAsync(serializer, buffer)
234-
235-
expect(result.join_ref).toBe('123')
236-
expect(result.ref).toBeNull()
237-
expect(result.topic).toBe('top')
238-
expect(result.event).toBe('some-event')
239-
expect(result.payload.constructor).toBe(Object)
240-
expect(result.payload).toStrictEqual({ a: 'b' })
241-
})
242-
243-
it('decodes reply payload as JSON', async () => {
244-
let bin = '\x01\x03\x02\x03\x0210012topok{"a":"b"}'
245-
let buffer = new TextEncoder().encode(bin).buffer
246-
247-
const result = await decodeAsync(serializer, buffer)
248-
249-
expect(result.join_ref).toBe('100')
250-
expect(result.ref).toBe('12')
251-
expect(result.topic).toBe('top')
252-
expect(result.event).toBe('phx_reply')
253-
expect(result.payload.status).toBe('ok')
254-
expect(result.payload.response.constructor).toBe(Object)
255-
expect(result.payload.response).toStrictEqual({ a: 'b' })
256-
})
257-
258-
it('decodes broadcast payload as JSON', async () => {
259-
let bin = '\x02\x03\ntopsome-event{"a":"b"}'
260-
let buffer = new TextEncoder().encode(bin).buffer
261-
262-
const result = await decodeAsync(serializer, buffer)
263-
264-
expect(result.join_ref).toBeNull()
265-
expect(result.ref).toBeNull()
266-
expect(result.topic).toBe('top')
267-
expect(result.event).toBe('some-event')
268-
expect(result.payload.constructor).toBe(Object)
269-
expect(result.payload).toStrictEqual({ a: 'b' })
270-
})
271-
272178
it('decodes user broadcast with JSON payload and no metadata', async () => {
273179
// 4 -> user_broadcast
274180
// 3 for topic length

0 commit comments

Comments
 (0)