Skip to content
Open
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
38 changes: 28 additions & 10 deletions assets/js/phoenix/serializer.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,28 +30,46 @@ export default {

binaryEncode(message){
let {join_ref, ref, event, topic, payload} = message
let metaLength = this.META_LENGTH + join_ref.length + ref.length + topic.length + event.length
let encoder = new TextEncoder()
let joinRefBytes = encoder.encode(join_ref)
let refBytes = encoder.encode(ref)
let topicBytes = encoder.encode(topic)
let eventBytes = encoder.encode(event)

this.assertFieldSize(joinRefBytes.byteLength, "join_ref")
this.assertFieldSize(refBytes.byteLength, "ref")
this.assertFieldSize(topicBytes.byteLength, "topic")
this.assertFieldSize(eventBytes.byteLength, "event")

let metaLength = this.META_LENGTH + joinRefBytes.byteLength + refBytes.byteLength + topicBytes.byteLength + eventBytes.byteLength
let header = new ArrayBuffer(this.HEADER_LENGTH + metaLength)
let headerBytes = new Uint8Array(header)
let view = new DataView(header)
let offset = 0

view.setUint8(offset++, this.KINDS.push) // kind
view.setUint8(offset++, join_ref.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(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)))
view.setUint8(offset++, joinRefBytes.byteLength)
view.setUint8(offset++, refBytes.byteLength)
view.setUint8(offset++, topicBytes.byteLength)
view.setUint8(offset++, eventBytes.byteLength)
headerBytes.set(joinRefBytes, offset); offset += joinRefBytes.byteLength
headerBytes.set(refBytes, offset); offset += refBytes.byteLength
headerBytes.set(topicBytes, offset); offset += topicBytes.byteLength
headerBytes.set(eventBytes, offset); offset += eventBytes.byteLength

var combined = new Uint8Array(header.byteLength + payload.byteLength)
combined.set(new Uint8Array(header), 0)
combined.set(headerBytes, 0)
combined.set(new Uint8Array(payload), header.byteLength)

return combined.buffer
},

assertFieldSize(size, name){
if(size > 255){
throw new Error(`unable to convert ${name} to binary: must be less than or equal to 255 bytes, but is ${size} bytes`)
}
},

binaryDecode(buffer){
let view = new DataView(buffer)
let kind = view.getUint8(0)
Expand Down
42 changes: 42 additions & 0 deletions assets/test/serializer_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,48 @@ describe("binary", () => {
})
})

it("encodes non-ASCII metadata as UTF-8", (done) => {
let buffer = binPayload()
let topic = "room:café"
let event = "π"
let joinRef = "🚀"
let ref = "1"
let encoder = new TextEncoder()
let topicBytes = encoder.encode(topic)
let eventBytes = encoder.encode(event)
let joinRefBytes = encoder.encode(joinRef)
let refBytes = encoder.encode(ref)

Serializer.encode({join_ref: joinRef, ref: ref, topic: topic, event: event, payload: buffer}, (result) => {
let bytes = new Uint8Array(result)
expect(bytes[0]).toBe(0) // push kind
expect(bytes[1]).toBe(joinRefBytes.byteLength) // 4 bytes for "🚀"
expect(bytes[2]).toBe(refBytes.byteLength)
expect(bytes[3]).toBe(topicBytes.byteLength) // 10 bytes for "room:café"
expect(bytes[4]).toBe(eventBytes.byteLength) // 2 bytes for "π"

let offset = 5
expect(Array.from(bytes.slice(offset, offset + joinRefBytes.byteLength))).toEqual(Array.from(joinRefBytes))
offset += joinRefBytes.byteLength
expect(Array.from(bytes.slice(offset, offset + refBytes.byteLength))).toEqual(Array.from(refBytes))
offset += refBytes.byteLength
expect(Array.from(bytes.slice(offset, offset + topicBytes.byteLength))).toEqual(Array.from(topicBytes))
offset += topicBytes.byteLength
expect(Array.from(bytes.slice(offset, offset + eventBytes.byteLength))).toEqual(Array.from(eventBytes))
offset += eventBytes.byteLength
expect(bytes[offset]).toBe(1) // payload byte
done()
})
})

it("throws when a metadata field exceeds 255 UTF-8 bytes", () => {
let buffer = binPayload()
let big = "a".repeat(256)
expect(() => {
Serializer.encode({join_ref: "0", ref: "1", topic: big, event: "e", payload: buffer}, () => {})
}).toThrow(/topic/)
})

it("decodes broadcast", (done) => {
let bin = "\x02\x03\ntopsome-event\x01\x01"
let buffer = new TextEncoder().encode(bin).buffer
Expand Down