Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 21 additions & 14 deletions packages/core/realtime-js/src/lib/serializer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
import { CHANNEL_EVENTS } from '../lib/constants'

export type Msg<T> = {
join_ref: string
ref: string
join_ref?: string | null
ref?: string | null
topic: string
event: string
payload: T
Expand Down Expand Up @@ -42,19 +42,22 @@ export default class Serializer {
}

private _binaryEncodePush(message: Msg<ArrayBuffer>) {
const { join_ref, ref, event, topic, payload } = message
const metaLength = this.META_LENGTH + join_ref.length + ref.length + topic.length + event.length
const { event, topic, payload } = message
const ref = message.ref ?? ''
const joinRef = message.join_ref ?? ''

const metaLength = this.META_LENGTH + joinRef.length + ref.length + topic.length + event.length

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

view.setUint8(offset++, this.KINDS.push) // kind
view.setUint8(offset++, join_ref.length)
view.setUint8(offset++, joinRef.length)
view.setUint8(offset++, ref.length)
view.setUint8(offset++, topic.length)
view.setUint8(offset++, event.length)
Array.from(join_ref, (char) => view.setUint8(offset++, char.charCodeAt(0)))
Array.from(joinRef, (char) => view.setUint8(offset++, char.charCodeAt(0)))
Array.from(ref, (char) => view.setUint8(offset++, char.charCodeAt(0)))
Array.from(topic, (char) => view.setUint8(offset++, char.charCodeAt(0)))
Array.from(event, (char) => view.setUint8(offset++, char.charCodeAt(0)))
Expand All @@ -75,13 +78,15 @@ export default class Serializer {
}

private _encodeBinaryUserBroadcastPush(message: Msg<{ event: string } & { [key: string]: any }>) {
const { join_ref, ref, topic } = message
const topic = message.topic
const ref = message.ref ?? ''
const joinRef = message.join_ref ?? ''
const userEvent = message.payload.event
const userPayload = message.payload?.payload ?? new ArrayBuffer(0)

const metaLength =
this.USER_BROADCAST_PUSH_META_LENGTH +
join_ref.length +
joinRef.length +
ref.length +
topic.length +
userEvent.length
Expand All @@ -91,12 +96,12 @@ export default class Serializer {
let offset = 0

view.setUint8(offset++, this.KINDS.userBroadcastPush) // kind
view.setUint8(offset++, join_ref.length)
view.setUint8(offset++, joinRef.length)
view.setUint8(offset++, ref.length)
view.setUint8(offset++, topic.length)
view.setUint8(offset++, userEvent.length)
view.setUint8(offset++, this.BINARY_ENCODING)
Array.from(join_ref, (char) => view.setUint8(offset++, char.charCodeAt(0)))
Array.from(joinRef, (char) => view.setUint8(offset++, char.charCodeAt(0)))
Array.from(ref, (char) => view.setUint8(offset++, char.charCodeAt(0)))
Array.from(topic, (char) => view.setUint8(offset++, char.charCodeAt(0)))
Array.from(userEvent, (char) => view.setUint8(offset++, char.charCodeAt(0)))
Expand All @@ -109,7 +114,9 @@ export default class Serializer {
}

private _encodeJsonUserBroadcastPush(message: Msg<{ event: string } & { [key: string]: any }>) {
const { join_ref, ref, topic } = message
const topic = message.topic
const ref = message.ref ?? ''
const joinRef = message.join_ref ?? ''
const userEvent = message.payload.event
const userPayload = message.payload?.payload ?? {}

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

const metaLength =
this.USER_BROADCAST_PUSH_META_LENGTH +
join_ref.length +
joinRef.length +
ref.length +
topic.length +
userEvent.length
Expand All @@ -128,12 +135,12 @@ export default class Serializer {
let offset = 0

view.setUint8(offset++, this.KINDS.userBroadcastPush) // kind
view.setUint8(offset++, join_ref.length)
view.setUint8(offset++, joinRef.length)
view.setUint8(offset++, ref.length)
view.setUint8(offset++, topic.length)
view.setUint8(offset++, userEvent.length)
view.setUint8(offset++, this.JSON_ENCODING)
Array.from(join_ref, (char) => view.setUint8(offset++, char.charCodeAt(0)))
Array.from(joinRef, (char) => view.setUint8(offset++, char.charCodeAt(0)))
Array.from(ref, (char) => view.setUint8(offset++, char.charCodeAt(0)))
Array.from(topic, (char) => view.setUint8(offset++, char.charCodeAt(0)))
Array.from(userEvent, (char) => view.setUint8(offset++, char.charCodeAt(0)))
Expand Down
93 changes: 93 additions & 0 deletions packages/core/realtime-js/test/serializer.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,13 @@ const decodeAsync = (
}

let exampleMsg = { join_ref: '0', ref: '1', topic: 't', event: 'e', payload: { foo: 1 } }
let missingRefExampleMsg = {
join_ref: null,
ref: null,
topic: 't',
event: 'e',
payload: { foo: 1 },
}

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

it('encodes missing refs', async () => {
const result = await encodeAsync(serializer, missingRefExampleMsg)
expect(result).toBe('[null,null,"t","e",{"foo":1}]')
})

it('decodes', async () => {
const result = await decodeAsync(serializer, '["0","1","t","e",{"foo":1}]')
expect(result).toEqual(exampleMsg)
})

it('decodes missing refs', async () => {
const result = await decodeAsync(serializer, '[null,null,"t","e",{"foo":1}]')
expect(result).toEqual(missingRefExampleMsg)
})
})

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

it('encodes push with undefined join_ref and ref', async () => {
let buffer = binPayload()
let bin = '\0\x00\x00\x01\x01te\x01\x04'
const result = await encodeAsync(serializer, {
join_ref: undefined,
ref: undefined,
topic: 't',
event: 'e',
payload: buffer,
})
expect(decoder.decode(result as ArrayBuffer)).toBe(bin)
})

it('encodes push with no join_ref no ref', async () => {
let buffer = binPayload()
let bin = '\0\x00\x00\x01\x01te\x01\x04'
const result = await encodeAsync(serializer, {
topic: 't',
event: 'e',
payload: buffer,
})
expect(decoder.decode(result as ArrayBuffer)).toBe(bin)
})

it('encodes variable length segments', async () => {
let buffer = binPayload()
let bin = '\0\x02\x01\x03\x02101topev\x01\x04'
Expand Down Expand Up @@ -106,6 +147,33 @@ describe('binary', () => {
expect(decoder.decode(result as ArrayBuffer)).toBe(bin)
})

it('encodes user broadcast push with JSON payload no refs', async () => {
// 3 -> user_broadcast_push
// 0 join_ref length
// 0 for ref length
// 3 for topic length
// 10 for user event length
// 1 for JSON encoding
// actual join ref
// actual ref
// actual topic
// actual user event
// actual payload
let bin = '\x03\x00\x00\x03\x0a\x01topuser-event{"a":"b"}'

const result = await encodeAsync(serializer, {
topic: 'top',
event: 'broadcast',
payload: {
event: 'user-event',
payload: {
a: 'b',
},
},
})
expect(decoder.decode(result as ArrayBuffer)).toBe(bin)
})

it('encodes user broadcast push with Binary payload', async () => {
// 3 -> user_broadcast_push
// 2 join_ref length
Expand Down Expand Up @@ -133,6 +201,31 @@ describe('binary', () => {
expect(decoder.decode(result as ArrayBuffer)).toBe(bin)
})

it('encodes user broadcast push with Binary payload no refs', async () => {
// 3 -> user_broadcast_push
// 0 join_ref length
// 0 for ref length
// 3 for topic length
// 10 for user event length
// 0 for Binary encoding
// actual join ref
// actual ref
// actual topic
// actual user event
// actual payload
let bin = '\x03\x00\x00\x03\x0a\x00topuser-event\x01\x04'

const result = await encodeAsync(serializer, {
topic: 'top',
event: 'broadcast',
payload: {
event: 'user-event',
payload: binPayload(),
},
})
expect(decoder.decode(result as ArrayBuffer)).toBe(bin)
})

it('decodes push payload as JSON', async () => {
let bin = '\0\x03\x03\n123topsome-event{"a":"b"}'
let buffer = new TextEncoder().encode(bin).buffer
Expand Down