Skip to content

Commit

Permalink
Fix unicode chunking issue, ignore BOM mark
Browse files Browse the repository at this point in the history
  • Loading branch information
rexxars committed Aug 23, 2018
1 parent b009a96 commit 1a91936
Show file tree
Hide file tree
Showing 3 changed files with 72 additions and 11 deletions.
37 changes: 27 additions & 10 deletions lib/eventsource.js
Expand Up @@ -10,6 +10,18 @@ var httpsOptions = [
'rejectUnauthorized', 'secureProtocol', 'servername'
]

var bom = [239, 187, 191]
var colon = 58
var space = 32
var lineFeed = 10
var carriageReturn = 13

function hasBom (buf) {
return bom.every(function (charCode, index) {
return buf[index] === charCode
})
}

/**
* Creates a new EventSource object
*
Expand Down Expand Up @@ -160,16 +172,21 @@ function EventSource (url, eventSourceInitDict) {

// text/event-stream parser adapted from webkit's
// Source/WebCore/page/EventSource.cpp
var buf = ''
var isFirst = true
var buf
res.on('data', function (chunk) {
buf += chunk
buf = buf ? Buffer.concat([buf, chunk]) : chunk
if (isFirst && hasBom(buf)) {
buf = buf.slice(bom.length)
}

isFirst = false
var pos = 0
var length = buf.length

while (pos < length) {
if (discardTrailingNewline) {
if (buf[pos] === '\n') {
if (buf[pos] === lineFeed) {
++pos
}
discardTrailingNewline = false
Expand All @@ -181,14 +198,14 @@ function EventSource (url, eventSourceInitDict) {

for (var i = pos; lineLength < 0 && i < length; ++i) {
c = buf[i]
if (c === ':') {
if (c === colon) {
if (fieldLength < 0) {
fieldLength = i - pos
}
} else if (c === '\r') {
} else if (c === carriageReturn) {
discardTrailingNewline = true
lineLength = i - pos
} else if (c === '\n') {
} else if (c === lineFeed) {
lineLength = i - pos
}
}
Expand All @@ -203,7 +220,7 @@ function EventSource (url, eventSourceInitDict) {
}

if (pos === length) {
buf = ''
buf = void 0
} else if (pos > 0) {
buf = buf.slice(pos)
}
Expand Down Expand Up @@ -245,19 +262,19 @@ function EventSource (url, eventSourceInitDict) {
} else if (fieldLength > 0) {
var noValue = fieldLength < 0
var step = 0
var field = buf.slice(pos, pos + (noValue ? lineLength : fieldLength))
var field = buf.slice(pos, pos + (noValue ? lineLength : fieldLength)).toString()

if (noValue) {
step = lineLength
} else if (buf[pos + fieldLength + 1] !== ' ') {
} else if (buf[pos + fieldLength + 1] !== space) {
step = fieldLength + 1
} else {
step = fieldLength + 2
}
pos += step

var valueLength = lineLength - step
var value = buf.slice(pos, pos + valueLength)
var value = buf.slice(pos, pos + valueLength).toString()

if (field === 'data') {
data += value + '\n'
Expand Down
1 change: 1 addition & 0 deletions package.json
Expand Up @@ -30,6 +30,7 @@
}
],
"devDependencies": {
"buffer-from": "^1.1.1",
"express": "^4.15.3",
"mocha": "^3.5.3",
"nyc": "^11.2.1",
Expand Down
45 changes: 44 additions & 1 deletion test/eventsource_test.js
@@ -1,5 +1,6 @@
/* eslint-disable no-new */
var EventSource = require('../lib/eventsource')
var bufferFrom = require('buffer-from')
var path = require('path')
var http = require('http')
var https = require('https')
Expand Down Expand Up @@ -167,6 +168,24 @@ describe('Parser', function () {
})
})

it('ignores byte-order mark', function (done) {
createServer(function (err, server) {
if (err) return done(err)

server.on('request', function (req, res) {
res.writeHead(200, {'Content-Type': 'text/event-stream'})
res.write('\uFEFF')
res.write('data: foo\n\n')
res.end()
})
var es = new EventSource(server.url)
es.onmessage = function (m) {
assert.equal('foo', m.data)
server.close(done)
}
})
})

it('parses one one-line message in two chunks', function (done) {
createServer(function (err, server) {
if (err) return done(err)
Expand Down Expand Up @@ -215,7 +234,7 @@ describe('Parser', function () {
})
})

it('parses really chopped up unicode data', function (done) {
it('parses chopped up unicode data', function (done) {
createServer(function (err, server) {
if (err) return done(err)

Expand All @@ -237,6 +256,30 @@ describe('Parser', function () {
})
})

it('parses really chopped up unicode data', function (done) {
createServer(function (err, server) {
if (err) return done(err)

server.on('request', function (req, res) {
const msg = bufferFrom('data: Aslak Hellesøy is the original author\n\n')
res.writeHead(200, {'Content-Type': 'text/event-stream'})

// Slice in the middle of a unicode sequence (ø), making sure that one data
// chunk will contain the first byte and the second chunk will get the other
res.write(msg.slice(0, 19), 'binary', function () {
res.write(msg.slice(19))
})
})

var es = new EventSource(server.url)

es.onmessage = function (m) {
assert.equal('Aslak Hellesøy is the original author', m.data)
server.close(done)
}
})
})

it('accepts CRLF as separator', function (done) {
createServer(function (err, server) {
if (err) return done(err)
Expand Down

0 comments on commit 1a91936

Please sign in to comment.