Skip to content

Commit 9715f5d

Browse files
authored
comply with SSE spec regarding nulls, colons, and partial messages (#15)
1 parent c62e49c commit 9715f5d

File tree

4 files changed

+44
-9
lines changed

4 files changed

+44
-9
lines changed

Makefile

+2-2
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ start-contract-test-service-bg:
1212
@make start-contract-test-service >$(TEMP_TEST_OUTPUT) 2>&1 &
1313

1414
run-contract-tests:
15-
@curl -s https://raw.githubusercontent.com/launchdarkly/sse-contract-tests/v0.0.3/downloader/run.sh \
16-
| VERSION=v0 PARAMS="-url http://localhost:8000 -debug -stop-service-at-end" sh
15+
@curl -s https://raw.githubusercontent.com/launchdarkly/sse-contract-tests/v2.0.0/downloader/run.sh \
16+
| VERSION=v2 PARAMS="-url http://localhost:8000 -debug -stop-service-at-end" sh
1717

1818
contract-tests: build-contract-tests start-contract-test-service-bg run-contract-tests
1919

contract-tests/streamEntity.js

+4-3
Original file line numberDiff line numberDiff line change
@@ -81,9 +81,10 @@ function StreamEntity(options) {
8181
s.doCommand = params => {
8282
switch (params.command) {
8383
case 'listen':
84-
if (!listeningForType[params.type]) {
85-
listeningForType[params.type] = true;
86-
s.sse.addEventListener(params.type, s.onMessage);
84+
const eventType = params.listen.type;
85+
if (!listeningForType[eventType]) {
86+
listeningForType[eventType] = true;
87+
s.sse.addEventListener(eventType, s.onMessage);
8788
}
8889
return true;
8990

lib/eventsource.js

+13-4
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,7 @@ function EventSource (url, eventSourceInitDict) {
5757
}
5858

5959
var discardTrailingNewline = false
60-
var data = ''
61-
var eventName = ''
60+
var data, eventName, eventId
6261

6362
var reconnectUrl = null
6463
var retryDelayStrategy = new retryDelay.RetryDelayStrategy(
@@ -207,6 +206,10 @@ function EventSource (url, eventSourceInitDict) {
207206
return
208207
}
209208

209+
data = ''
210+
eventName = ''
211+
eventId = undefined
212+
210213
readyState = EventSource.OPEN
211214
res.on('close', function () {
212215
res.removeAllListeners('close')
@@ -336,16 +339,20 @@ function EventSource (url, eventSourceInitDict) {
336339
if (lineLength === 0) {
337340
if (data.length > 0) {
338341
var type = eventName || 'message'
342+
if (eventId !== undefined) {
343+
lastEventId = eventId
344+
}
339345
var event = new MessageEvent(type, {
340346
data: data.slice(0, -1), // remove trailing newline
341347
lastEventId: lastEventId,
342348
origin: original(url)
343349
})
344350
data = ''
351+
eventId = undefined
345352
receivedEvent(event)
346353
}
347354
eventName = void 0
348-
} else if (fieldLength > 0) {
355+
} else {
349356
var noValue = fieldLength < 0
350357
var step = 0
351358
var field = buf.slice(pos, pos + (noValue ? lineLength : fieldLength)).toString()
@@ -367,7 +374,9 @@ function EventSource (url, eventSourceInitDict) {
367374
} else if (field === 'event') {
368375
eventName = value
369376
} else if (field === 'id') {
370-
lastEventId = value
377+
if (!value.includes("\u0000")) {
378+
eventId = value
379+
}
371380
} else if (field === 'retry') {
372381
var retry = parseInt(value, 10)
373382
if (!Number.isNaN(retry)) {

test/eventsource_test.js

+25
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,19 @@ describe('Parser', () => {
310310
})
311311
})
312312

313+
it('treats field name without colon as a field with an empty value', async () => {
314+
await withServer(async server => {
315+
server.byDefault(writeEvents(['data\n\ndata\ndata\n\n']))
316+
317+
await withEventSource(server, async es => {
318+
await shouldReceiveMessages(es, [
319+
{ data: '' },
320+
{ data: '\n' }
321+
])
322+
})
323+
})
324+
})
325+
313326
it('causes entire event to be ignored for empty event field', async () => {
314327
await withServer(async server => {
315328
server.byDefault(writeEvents(['event:\n\ndata: Hello\n\n']))
@@ -954,6 +967,18 @@ describe('Events', function () {
954967
})
955968
})
956969

970+
it('ignores event ID that contains a null', async () => {
971+
await withServer(async server => {
972+
server.byDefault(writeEvents(['id: 12\u00003\ndata: hello\n\n']))
973+
974+
await withEventSource(server, async es => {
975+
const messages = startMessageQueue(es)
976+
const m = await messages.take()
977+
assert.equal(m.lastEventId, '')
978+
})
979+
})
980+
})
981+
957982
it('populates messages with enumerable properties so they can be inspected via console.log().', async () => {
958983
await withServer(async server => {
959984
server.byDefault(writeEvents(['data: World\n\n']))

0 commit comments

Comments
 (0)