2018-01-25 18:07:45 -08:00
|
|
|
// adpated from https://github.com/sebastianseilund/node-json-socket
|
|
|
|
|
2019-01-01 16:53:12 -08:00
|
|
|
import { StringDecoder } from 'string_decoder'
|
2018-01-21 19:55:47 -08:00
|
|
|
import EventEmitter from 'events'
|
2018-02-11 19:58:22 -08:00
|
|
|
import btc from 'better-try-catch'
|
2018-01-21 19:55:47 -08:00
|
|
|
|
|
|
|
const decoder = new StringDecoder()
|
|
|
|
|
2019-01-01 16:53:12 -08:00
|
|
|
/**
|
|
|
|
* JsonStream - Description
|
|
|
|
* @extends EventEmitter
|
|
|
|
*/
|
|
|
|
class JsonStream extends EventEmitter {
|
|
|
|
constructor(opts = {}) {
|
2018-01-21 19:55:47 -08:00
|
|
|
super()
|
|
|
|
this._contentLength = null
|
|
|
|
this._buffer = ''
|
|
|
|
this._delimeter = opts.delimiter || '#'
|
|
|
|
this.onData = this.onData.bind(this)
|
2018-02-12 14:41:06 -08:00
|
|
|
this.serialize = this.serialize.bind(this)
|
2018-01-21 19:55:47 -08:00
|
|
|
}
|
|
|
|
|
2019-01-01 16:53:12 -08:00
|
|
|
onData(data) {
|
2018-02-12 14:41:06 -08:00
|
|
|
// console.log('a chunk arrived', data)
|
2018-01-21 19:55:47 -08:00
|
|
|
data = decoder.write(data)
|
|
|
|
try {
|
|
|
|
this._handleData(data)
|
|
|
|
} catch (e) {
|
|
|
|
this.emit('error', { error: e })
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-02-11 19:58:22 -08:00
|
|
|
async serialize(message) {
|
2019-01-01 16:53:12 -08:00
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
let [err, messageData] = btc(JSON.stringify)(message)
|
2018-02-11 19:58:22 -08:00
|
|
|
if (err) reject(err)
|
2019-01-01 16:53:12 -08:00
|
|
|
let [err2, length] = btc(Buffer.byteLength)(messageData, 'utf8')
|
2018-02-11 19:58:22 -08:00
|
|
|
if (err2) reject(err2)
|
|
|
|
let data = length + this._delimeter + messageData
|
2018-02-12 14:41:06 -08:00
|
|
|
// console.log('serialized',data)
|
|
|
|
resolve(data)
|
2018-02-11 19:58:22 -08:00
|
|
|
})
|
2018-01-21 19:55:47 -08:00
|
|
|
}
|
|
|
|
|
2019-01-01 16:53:12 -08:00
|
|
|
_handleData(data) {
|
2018-01-21 19:55:47 -08:00
|
|
|
this._buffer += data
|
|
|
|
if (this._contentLength == null) {
|
|
|
|
var i = this._buffer.indexOf(this._delimeter)
|
2018-02-12 14:41:06 -08:00
|
|
|
//Check if the buffer has a this._opts.delimeter or "#", if not, the end of the buffer string might be in the middle of a content length string
|
2018-01-21 19:55:47 -08:00
|
|
|
if (i !== -1) {
|
|
|
|
var rawContentLength = this._buffer.substring(0, i)
|
|
|
|
this._contentLength = parseInt(rawContentLength)
|
|
|
|
if (isNaN(this._contentLength)) {
|
|
|
|
this._contentLength = null
|
|
|
|
this._buffer = ''
|
2019-01-01 16:53:12 -08:00
|
|
|
var err = new Error(
|
|
|
|
'Invalid content length supplied (' +
|
|
|
|
rawContentLength +
|
|
|
|
') in: ' +
|
|
|
|
this._buffer
|
|
|
|
)
|
2018-01-21 19:55:47 -08:00
|
|
|
err.code = 'E_INVALID_CONTENT_LENGTH'
|
|
|
|
throw err
|
|
|
|
}
|
2019-01-01 16:53:12 -08:00
|
|
|
this._buffer = this._buffer.substring(i + 1)
|
2018-01-21 19:55:47 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if (this._contentLength != null) {
|
|
|
|
var length = Buffer.byteLength(this._buffer, 'utf8')
|
|
|
|
if (length == this._contentLength) {
|
|
|
|
this._handleMessage(this._buffer)
|
|
|
|
} else if (length > this._contentLength) {
|
|
|
|
var message = this._buffer.substring(0, this._contentLength)
|
|
|
|
var rest = this._buffer.substring(this._contentLength)
|
|
|
|
this._handleMessage(message)
|
|
|
|
this.onData(rest)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-01 16:53:12 -08:00
|
|
|
_handleMessage(data) {
|
2018-01-21 19:55:47 -08:00
|
|
|
this._contentLength = null
|
|
|
|
this._buffer = ''
|
|
|
|
var message
|
|
|
|
try {
|
|
|
|
message = JSON.parse(data)
|
|
|
|
} catch (e) {
|
2019-01-01 16:53:12 -08:00
|
|
|
var err = new Error(
|
|
|
|
'Could not parse JSON: ' + e.message + '\nRequest data: ' + data
|
|
|
|
)
|
2018-01-21 19:55:47 -08:00
|
|
|
err.code = 'E_INVALID_JSON'
|
|
|
|
throw err
|
|
|
|
}
|
|
|
|
message = message || {}
|
|
|
|
this.emit('message', message)
|
|
|
|
}
|
|
|
|
}
|
2019-01-01 16:53:12 -08:00
|
|
|
|
|
|
|
export default JsonStream
|