diffusion
Version:
Diffusion JavaScript client
194 lines (164 loc) • 5.18 kB
JavaScript
/*eslint valid-jsdoc: "off"*/
var inherits = require('inherits');
var BufferOutputStream = require('io/buffer-output-stream');
var BufferInputStream = require('io/buffer-input-stream');
var MessageEncoding = require('util/message-encoding');
var EncodingType = require('util/message-encoding').EncodingType;
var logger = require('util/logger').create('Message');
var ClientCapabilities = require('protocol/consts').CAPABILITIES;
var Zlib;
if (ClientCapabilities === 10) {
Zlib = require('util/zlib-decompression');
}
else {
logger.warn("Per message compression is disabled because 'zlib' bundle is not included");
Zlib = null;
}
var systemHeaders = {
0: [],
1: [],
2: [],
4: [],
5: [],
28: [],
29: [],
34: ['topic'],
};
var types = {
SERVICE_REQUEST: 0,
SERVICE_RESPONSE: 1,
SERVICE_ERROR: 2,
TOPIC_VALUE: 4,
TOPIC_DELTA: 5,
ABORT_NOTIFICATION: 28,
CLOSE_REQUEST: 29,
FETCH_REPLY: 34,
};
var encoding = {
NONE: 0,
ENCRYPTION_REQUESTED: 1,
COMPRESSION_REQUESTED: 2,
BASE64_REQUESTED: 3,
ENCRYPTED: 17,
COMPRESSED: 18,
BASE64: 19
};
var parse = function (buffer, callback) {
var bis = new BufferInputStream(buffer);
var typeAndEncoding = bis.read();
var type = MessageEncoding.extractMessageType(typeAndEncoding);
var encodingType = MessageEncoding.extractMessageEncoding(typeAndEncoding);
if (systemHeaders[type] === undefined) {
callback(new Error('Invalid message type: ' + type), null);
}
if (type === types.SERVICE_REQUEST || type === types.SERVICE_RESPONSE || type === types.SERVICE_ERROR) {
callback(null, parseServiceMessage(type, bis));
}
else if (type === types.TOPIC_VALUE || type === types.TOPIC_DELTA) {
parseClientTopicMessage(type, encodingType, bis, callback);
}
else {
var headers = bis.readUntil(0x01).toString().split('\u0002');
var body = bis.readMany(bis.count);
var fields = {};
if (systemHeaders[type]) {
systemHeaders[type].forEach(function (key) {
fields[key] = headers.shift();
});
}
callback(null, new Message(type, encoding.NONE, fields, body, headers));
}
};
function parseServiceMessage(type, bis) {
var headers = [];
var body = bis.readMany(bis.count);
var fields = {};
return new Message(type, encoding.NONE, fields, body, headers);
}
function parseClientTopicMessage(type, encodingType, bis, callback) {
var headers = [];
var fields = { 'id': bis.readInt32() };
if (encodingType === EncodingType.ZLIB && Zlib !== null) {
try {
var bytes = Zlib.decompressSync(bis.readMany(bis.count));
callback(null, new Message(type, encoding.COMPRESSED, fields, bytes, headers));
} catch(error) {
callback(error, null);
}
}
else {
callback(null,
new Message(type, encoding.NONE, fields, bis.readMany(bis.count), headers));
}
}
/**
* Create a writeable Message.
*
* The returned value inherits functionality from BufferOutputStream.
*
* @param {Number|Object} - Provide a number to specify a message type; use an object to provide fields
* @returns {Message} The created message
*/
var create = function (type) {
var fields;
if (type === undefined || (typeof type === "number" && systemHeaders[type] === undefined)) {
throw "Invalid message type: " + type;
} else if (type instanceof Object) {
fields = type;
} else {
fields = {type: type};
}
fields.encoding = fields.encoding || 0;
fields.headers = fields.headers || [];
fields.buffer = fields.buffer || new Buffer(0);
return new WriteableMessage(fields);
};
var getHeaderString = function (message) {
var sh = [];
var headers = '';
systemHeaders[message.type].forEach(function (h) {
sh.push(message[h]);
});
message.headers.forEach(function (h) {
sh.push(h);
});
// CLOSE_REQUEST has no header or body
if (systemHeaders[message.type].length === 0) {
return headers;
}
if (sh.length > 0) {
headers = sh.join('\u0002');
headers += '\u0001';
}
return headers;
};
function writeToBuffer(message, bos) {
bos.write(message.type);
bos.writeString(getHeaderString(message));
bos.writeMany(message.getBuffer());
}
function WriteableMessage(fields) {
Message.call(this, fields.type, fields.encoding, fields, fields.buffer, fields.headers);
BufferOutputStream.call(this, fields.buffer);
}
inherits(WriteableMessage, BufferOutputStream);
function Message(type, encoding, fields, data, headers) {
this.type = type;
this.encoding = encoding;
for (var f in fields) {
this[f] = fields[f];
}
this.data = data;
this.headers = headers;
}
Message.prototype.getInputStream = function () {
return new BufferInputStream(this.data);
};
module.exports = {
types: types,
parse: parse,
create: create,
encoding: encoding,
getHeaderString: getHeaderString,
writeToBuffer: writeToBuffer
};