infinispan-cere
Version:
Infinispan Javascript client
997 lines (885 loc) • 33.6 kB
JavaScript
'use strict';
(function() {
var _ = require('underscore');
var f = require('./functional');
var u = require('./utils');
var codec = require('./codec');
var logger = u.logger('protocols');
var INFINITE_LIFESPAN = 0x01, INFINITE_MAXIDLE = 0x02; // Duration flag masks
var MAGIC = 0xA0;
function decodePairActions(decoderKey, decoderValue) {
return f.actions(
[
decoderKey.fun(decoderKey.obj)
, decoderValue.fun(decoderValue.obj)
]
, function(values) {
if (values.length < 2) {
logger.tracef("Not enough to read (not array): %s", values);
return undefined;
}
return {key: values[0], value: values[1]};
});
}
var DECODE_STRING_PAIR = f.actions(
[codec.decodeString(), codec.decodeString()],
function(values) {
if (values.length < 2) {
logger.tracef("Not enough to read (not array): %s", values);
return undefined;
}
var pair = {};
pair[values[0]] = parseInt(values[1]);
return pair;
});
var DECODE_STRING = f.actions([codec.decodeString()], codec.lastDecoded);
var DECODE_TIMESTAMP = f.actions([codec.decodeLong(), codec.decodeVInt()], codec.allDecoded(2));
var DECODE_UBYTE = f.actions([codec.decodeUByte()], codec.lastDecoded);
function hasOpt(opts, name) { return _.has(opts, name) && f.truthy(opts[name]); }
function hasOptPrev(opts) { return hasOpt(opts, 'previous'); }
function decodeTimestamp(flags, mask, headers, bytebuf) {
var timestamp;
if (((flags & mask) != mask)) {
var decoded = DECODE_TIMESTAMP(bytebuf);
if (decoded.length < 2)
return undefined;
timestamp = decoded;
} else {
timestamp = [-1, -1];
}
return _.object(headers, timestamp);
}
function decodeSingle(decoder) {
return f.actions([decoder.fun(decoder.obj)], codec.lastDecoded);
}
function decoderMedia(obj) {
return {
obj: obj
, fun: f.invoker('decodeMedia', obj.decodeMedia)
};
}
var EncodeMixin = (function() {
var logger = u.logger('encoder');
return {
buildFlags: function (opts) { // TODO: Move out to a Mixin (similar to expiry)
return hasOptPrev(opts) ? 0x01 : 0;
},
encodeHeader: function (op, topologyId, opts) {
logger.tracef('Encode operation with topology id %d', topologyId);
var protocolVersion = this.version;
var cacheName = this.clientOpts['cacheName'];
var flags = this.buildFlags(opts);
var clientIntelligence = this.clientOpts['topologyUpdates'] ? 3 : 1;
return function(id) {
return [
codec.encodeUByte(MAGIC), // magic
codec.encodeVLong(id), // msg id
codec.encodeUByte(protocolVersion), // version
codec.encodeUByte(op), // op code
codec.encodeString(cacheName), // cache name
codec.encodeVInt(f.existy(flags) ? flags : 0), // flags
codec.encodeUByte(clientIntelligence), // basic client intelligence
codec.encodeVInt(topologyId) // client topology id
];
}
},
encodeKey: function (k) {
var outer = this;
return function() {
return [outer.encodeMediaKey(k)]; // key
}
},
encodeKeyVersion: function (k, version) {
var outer = this;
return function() {
return [outer.encodeMediaKey(k), codec.encodeBytes(version)]; // key + version
}
},
encodeKeyValue: function (k, v) {
var outer = this;
return function(opts) {
return f.cat(
[outer.encodeMediaKey(k)], // key
outer.encodeExpiry(opts), // lifespan & max idle
[outer.encodeMediaValue(v)] // value
);
}
},
encodeKeyValueVersion: function (k, v, version) {
var outer = this;
return function(opts) {
return f.cat(
[outer.encodeMediaKey(k)], // key
outer.encodeExpiry(opts), // lifespan & max idle
[codec.encodeBytes(version), // version
outer.encodeMediaValue(v)] // value
);
}
},
encodeMultiKey: function (keys) {
var outer = this;
return function() {
var base = [
codec.encodeVInt(_.size(keys)) // key count
];
var withKeys = _.map(keys, function (key) {
return outer.encodeMediaKey(key); // key
});
return f.cat(base, withKeys);
};
},
encodeMultiKeyValue: function (pairs) {
var outer = this;
return function(opts) {
var withPairs = _.map(pairs, function (pair) {
return [
outer.encodeMediaKey(pair.key), // key
outer.encodeMediaValue(pair.value) // value
]
});
return f.cat(
outer.encodeExpiry(opts), // lifespan & max idle
[codec.encodeVInt(_.size(pairs))], // entry count
_.flatten(withPairs)); // key + value pairs
};
},
encodeNameParams: function(name, params) {
var outer = this;
return function() {
var steps = [codec.encodeString(name), codec.encodeVInt(_.keys(params).length)];
_.mapObject(params, function(val, key) {
steps.push(codec.encodeString(key));
steps.push(outer.encodeMediaValue(val));
});
return steps;
};
},
stepsHeader: function(ctx, op, opts) {
var header = this.encodeHeader(op, ctx.topologyId, opts)(ctx.id);
var mediaType = this.encodeMediaTypes();
return f.cat(header, mediaType);
},
stepsHeaderBody: function(ctx, op, body, opts) {
var header = this.stepsHeader(ctx, op, opts, ctx.topologyId);
return f.cat(header, body(opts));
}
};
}());
var ExpiryEncodeMixin = (function() {
function parseDuration(d) {
if (!f.existy(d)) {
return [undefined, 7];
} else if(_.isNumber(d)) {
// Numeric durations only allowed to describe infinite (negative) or default durations (0)
if (d < 0) return [undefined, 8];
else if (d == 0) return [undefined, 7];
else throw new Error('Positive duration provided without time unit: ' + d);
} else {
var splitter = /(\d+)[\s,]*([a-zμ]+)/g;
var matches = splitter.exec(d);
if (f.existy(matches))
return [parseInt(matches[1]), timeUnitToByte(matches[2])];
else
throw new Error('Unknown duration format for ' + d);
}
}
function timeUnitToByte(unit) {
switch (unit) {
case 's': return 0;
case 'ms': return 1;
case 'ns': return 2;
case 'μs': return 3;
case 'm': return 4;
case 'h': return 5;
case 'd': return 6;
default: // TODO: Could it be caught in regular expression?
throw new Error('Unknown duration unit in ' + unit);
}
}
return {
encodeExpiry: function (opts) {
if (f.existy(opts)) {
var lifespan = parseDuration(opts['lifespan']);
var maxIdle = parseDuration(opts['maxIdle']);
// The way lifespan/maxIdle definitions are interleaved is messy :|
var steps = [codec.encodeUByte(lifespan[1] << 4 | maxIdle[1])];
if (f.existy(lifespan[0])) steps.push(codec.encodeVInt(lifespan[0]));
if (f.existy(maxIdle[0])) steps.push(codec.encodeVInt(maxIdle[0]));
return steps;
}
return [codec.encodeUByte(0x77)]; // default lifespan & max idle
}
}
}());
var DecodeMixin = (function() {
var logger = u.logger('decoder');
var DECODE_HEADER = f.actions(
[codec.decodeUByte(), // magic
codec.decodeVLong(), // msg id
codec.decodeUByte(), // op code
codec.decodeUByte(), // status
codec.decodeUByte() // topology change marker
], function(values) {
if (values.length < 5) {
logger.tracef("Not enough to read (not array): %s", values);
return undefined;
}
return {
msgId: values[1], opCode: values[2],
status: values[3], hasNewTopology: values[4] == 1
}
});
// var DECODE_VERSIONED = f.actions([codec.decodeFixedBytes(8), codec.decodeObject()],
// function(values) { return {version: values[0], value: values[1]}; });
var DECODE_VINT = f.actions([codec.decodeVInt()], codec.lastDecoded);
var DECODE_TOPO_HEADER = f.actions(
[codec.decodeVInt(), codec.decodeVInt()],
function(values) {
if (values.length < 2) {
logger.tracef("Not enough to read (not array): %s", values);
return undefined;
}
return {id: values[0], numServers: values[1]};
});
var DECODE_HASH_HEADER = f.actions(
[codec.decodeUByte(), codec.decodeVInt()],
function(values) {
if (values.length < 2) {
logger.tracef("Not enough to read (not array): %s", values);
return undefined;
}
return {hashFunct: values[0], numSegments: values[1]};
});
var DECODE_HOST_PORT = f.actions([codec.decodeString(), codec.decodeShort()],
function(values) { return {host: values[0], port: values[1]}; });
var SUCCESS = 0x00, NOT_EXECUTED = 0x01, NOT_FOUND = 0x02, // Status codes
SUCCESS_WITH_PREV = 0x03, NOT_EXECUTED_WITH_PREV = 0x04; // Status codes
function isSuccess(status) {
return status == SUCCESS || status == SUCCESS_WITH_PREV;
}
function isNotExecuted(status) {
return status == NOT_EXECUTED || status == NOT_EXECUTED_WITH_PREV;
}
function hasPrevious(status) {
return status == SUCCESS_WITH_PREV || status == NOT_EXECUTED_WITH_PREV;
}
function hasError(header) { return header.opCode == 0x50; }
function decodePrev(header, bytebuf, decoder) {
var decodeActions = decodeSingle(decoder);
var prev = decodeActions(bytebuf);
if (f.existy(prev))
return {result: _.isEmpty(prev) ? undefined : prev, continue: true};
else
return {continue: false};
}
function decodeObject(header, bytebuf, action) {
if (isSuccess(header.status)) {
var obj = action(bytebuf);
if (f.existy(obj)) return {result: obj, continue: true};
else return {continue: false};
}
return {result: undefined, continue: true};
}
return {
decodeHeader: function(bytebuf) {
try {
var header = DECODE_HEADER(bytebuf);
if (f.existy(header)) {
logger.tracef("Read header(msgId=%d): opCode=%s, status=%s, hasNewTopology=%d",
header.msgId, header.opCode, header.status, header.hasNewTopology);
return {continue: true, result: header};
}
return {continue: false, result: undefined};
} catch(ex) {
logger.error('Error decoding header, message id unknown:', ex);
throw ex;
}
},
decodeError: function(header, bytebuf) {
var msg = DECODE_STRING(bytebuf);
logger.error('Error decoding body of request(msgId=%d): %s', header.msgId, msg);
return {continue: true, result: msg};
},
decodeBody: function(decoder, header, bytebuf, conn) {
logger.tracef('Call decode for request(msgId=%d)', header.msgId);
return f.existy(decoder)
? decoder(header, bytebuf, conn)
: {continue: true, result: undefined};
},
decodeValue: function() {
var decoderValue = decoderMedia(this.valueMediaType);
return function(header, bytebuf) {
var decodeAction = decodeSingle(decoderValue);
return decodeObject(header, bytebuf, decodeAction);
}
},
decodeWithMeta: function() {
var decoderValue = decoderMedia(this.valueMediaType);
return function(header, bytebuf) {
if (isSuccess(header.status)) {
var flags = DECODE_UBYTE(bytebuf);
if (!f.existy(flags))
return {continue: false};
logger.tracef('Decode with metadata, flags are: %d', flags);
var lifespan = decodeTimestamp(flags, INFINITE_LIFESPAN, ['created', 'lifespan'], bytebuf);
if (!f.existy(lifespan))
return {continue: false};
var idle = decodeTimestamp(flags, INFINITE_MAXIDLE, ['lastUsed', 'maxIdle'], bytebuf);
if (!f.existy(idle))
return {continue: false};
var decoder = f.actions(
[codec.decodeFixedBytes(8), decoderValue.fun(decoderValue.obj)]
, function(values) {
return {version: values[0], value: values[1]};
});
var versioned = decoder(bytebuf);
return f.existy(versioned)
? {result: f.merge(versioned, lifespan, idle), continue: true}
: {continue: false};
}
return {result: undefined, continue: true};
}
},
decodePrevOrElse: function(opts, cond, orElse) {
var decoderValue = decoderMedia(this.valueMediaType);
var shouldReturnPrev = hasOptPrev(opts);
return function(header, bytebuf) {
if (shouldReturnPrev)
return cond(header)
? decodePrev(header, bytebuf, decoderValue)
: {result: undefined, continue: true};
return orElse(header);
}
},
decodeCountValues: function() {
var decoderKey = decoderMedia(this.keyMediaType);
var decoderValue = decoderMedia(this.valueMediaType);
return function(header, bytebuf) {
var count = DECODE_VINT(bytebuf);
var pairs = [], i = 0;
while (i++ < count) {
var pair = decodePairActions(decoderKey, decoderValue)(bytebuf);
if (f.existy(pair)) pairs.push(pair);
else return {continue: false};
}
return {result: pairs, continue: true};
}
},
decodeStringPairs: function(header, bytebuf) {
var count = DECODE_VINT(bytebuf);
var pairs = [], i = 0;
while (i++ < count) {
var pair = DECODE_STRING_PAIR(bytebuf);
if (f.existy(pair)) pairs.push(pair);
else return {continue: false};
}
var obj = _.reduce(pairs, function(o, pair) {
return f.merge(pair, o);
}, {});
return {result: obj, continue: true};
},
decodeTopology: function(bytebuf) {
var topologyHeader = DECODE_TOPO_HEADER(bytebuf);
if (!f.existy(topologyHeader))
return {done: false};
var addrs = [], i = 0;
while (i++ < topologyHeader.numServers) {
var addr = DECODE_HOST_PORT(bytebuf);
if (f.existy(addr)) addrs.push(addr);
else return {done: false};
}
var hashHeader = DECODE_HASH_HEADER(bytebuf);
if (!f.existy(hashHeader))
return {done: false};
var segs = new Array(hashHeader.numSegments);
for (var j = 0; j < hashHeader.numSegments; j++) {
var numOwners = DECODE_UBYTE(bytebuf);
if (f.existy(numOwners)) {
segs[j] = new Array(numOwners);
for (var k = 0; k < numOwners; k++) {
var memberIndex = DECODE_VINT(bytebuf);
if (f.existy(memberIndex)) segs[j][k] = addrs[memberIndex];
else return {done: false};
}
} else {
return {done: false};
}
}
return {id: topologyHeader.id, servers: addrs, segments: segs, done: true};
},
decodeVInt: function(header, bytebuf) {
var num = DECODE_VINT(bytebuf);
return {result: num, continue: true};
},
hasSuccess: function(header) {
return isSuccess(header.status);
},
hasNotExecuted: function(header) {
return isNotExecuted(header.status);
},
hasPrevious: function(header) {
return hasPrevious(header.status);
},
isEvent: function(header) {
//return ((op(header) >> 4) & 0x06) == 0x06;
return ((header.opCode >> 4) & 0x06) == 0x06;
},
isError: function(header) {
return header.opCode == 0x50;
},
complete: function(f) {
return function(header) {
return {result: f(header), continue: true};
}
}
}
}());
var ListenersMixin = (function() {
var logger = u.logger('protocols.listeners');
var DECODE_EVENT_COMMON = f.actions(
[codec.decodeString(), // listener id
codec.decodeUByte(), // custom event marker
codec.decodeUByte()], // event is retried
function(values) {
if (values.length < 3) {
logger.tracef("Not enough to read (not array): %s", values);
return undefined;
}
return {listenerId: values[0], isCustom: values[1] == 1, isRetried: values[2] == 1}
});
function emitCustomOr(isCustom, decoderBytes, alternativeEmit) {
if (isCustom) {
return function(event, emitter, bytebuf, listenerId) {
var decoder = f.actions(
[decoderBytes.fun(decoderBytes.obj)]
, function(values) {
return {result: values[0]};
}
);
var custom = decoder(bytebuf);
logger.tracel(function() {
return ['Try to emit %s event %s', event, custom.result]; });
// TODO workaround ISPN-10166: key/value/prev should be JSON
if (_.has(custom.result, '_type')) {
custom.result.key = JSON.parse(custom.result.key);
custom.result.value = JSON.parse(custom.result.value);
custom.result.prev = JSON.parse(custom.result.prev);
}
var success = emitter.emit(event, custom.result, listenerId);
if (success) logger.tracef('Event emitted');
else logger.tracef('No listener defined for %s event', event);
return true;
};
}
return alternativeEmit(decoderBytes);
}
function emitKeyVersion(decoderKey) {
return function(event, emitter, bytebuf, listenerId) {
var decoder = f.actions(
[decoderKey.fun(decoderKey.obj), codec.decodeFixedBytes(8)]
, function(values) {
return {key: values[0], version: values[1]};
}
);
var keyVersion = decoder(bytebuf);
logger.tracel(function() {
return ['Try to emit %s event for key=%s and version=%s',
event, keyVersion.key, keyVersion.version.toString('hex')]; });
var success = emitter.emit(event, keyVersion.key, keyVersion.version, listenerId);
if (success) logger.tracef('Event emitted');
else logger.tracef('No listener defined for %s event', event);
return true;
};
}
function emitKey(decodeFn) {
return function(event, emitter, bytebuf, listenerId) {
var decodeActions = decodeSingle(decodeFn);
var key = decodeActions(bytebuf);
logger.tracef('Emit %s event for key=%s', event, key);
emitter.emit(event, key, listenerId);
return true;
}
}
return {
findConnectionListener: function(listenerId) {
var l = listeners.get(listenerId);
return f.existy(l) ? l.conn : undefined;
},
removeListeners: function(listenerId) {
var l = listeners.get(listenerId);
if (f.existy(l)) {
l.emitter.removeAllListeners();
listeners.remove(listenerId);
}
},
encodeListenerAdd: function(listenerId, opts) {
var includeState = hasOpt(opts, 'includeState') ? 1 : 0;
var steps = [
codec.encodeString(listenerId), // listener id
codec.encodeUByte(includeState), // include state
codec.encodeUByte(0) // TODO filter factory name
];
if (_.has(opts, "converterFactory") && _.has(opts.converterFactory, "name")) {
steps.push(codec.encodeString(opts.converterFactory.name));
steps.push(codec.encodeUByte(0)); // TODO add converter parameter support
} else {
steps.push(codec.encodeUByte(0)); // no converter
}
steps.push(codec.encodeUByte(0)); // raw data disabled
return function() {
return steps;
}
},
encodeListenerId: function(listenerId) {
return function() {
return [codec.encodeString(listenerId)]; // listener id
}
},
decodeEvent: function(header, bytebuf, listeners) {
var common = DECODE_EVENT_COMMON(bytebuf);
if (!f.existy(common))
return false;
var listenerId = common.listenerId;
if (f.existy(listenerId)) {
var decoderKey = decoderMedia(this.keyMediaType);
var dispatcher = f.dispatch(
f.isa(0x60, listeners.dispatchEvent('create', listenerId, bytebuf, emitCustomOr(common.isCustom, decoderKey, emitKeyVersion))),
f.isa(0x61, listeners.dispatchEvent('modify', listenerId, bytebuf, emitCustomOr(common.isCustom, decoderKey, emitKeyVersion))),
f.isa(0x62, listeners.dispatchEvent('remove', listenerId, bytebuf, emitCustomOr(common.isCustom, decoderKey, emitKey))),
f.isa(0x63, listeners.dispatchEvent('expiry', listenerId, bytebuf, emitCustomOr(common.isCustom, decoderKey, emitKey)))
);
return dispatcher(header.opCode);
}
return true;
}
}
}());
var IteratorMixin = (function() { // protocol 2.3+
var logger = u.logger('iterator');
var DECODE_SEGMENTS_COUNT = f.actions(
[codec.decodeVariableBytes(), // segments byte array
codec.decodeVInt()], // number of entries
function(values) {
if (values.length < 2) {
logger.tracef("Not enough to read (not array): %s", values);
return undefined;
}
return {segments: values[0], count: values[1]}
});
var DECODE_VERSION = f.actions(
[codec.decodeFixedBytes(8)],
function(values) { return {version: values[0]}; });
var DECODE_VINT = f.actions([codec.decodeVInt()], codec.lastDecoded);
return {
encodeIterStart: function (batchSize, opts) {
var meta = hasOpt(opts, 'metadata') ? 1 : 0;
return function() {
return [
codec.encodeSignedInt(-1), // segments
codec.encodeSignedInt(-1), // filter/converter factory
codec.encodeVInt(batchSize), // batch size
codec.encodeUByte(meta) // metadata
];
}
},
encodeIterId: function(iterId) {
return function() {
return [codec.encodeString(iterId)];
}
},
decodeIterId: function(header, bytebuf, conn) {
var iterId = DECODE_STRING(bytebuf);
return {result: {iterId: iterId, conn: conn}, continue: true};
},
decodeNextEntries: function() {
var decoderKey = decoderMedia(this.keyMediaType);
var decoderValue = decoderMedia(this.valueMediaType);
return function(header, bytebuf) {
var segmentsAndCount = DECODE_SEGMENTS_COUNT(bytebuf);
if (!f.existy(segmentsAndCount))
return {continue: false};
var count = segmentsAndCount.count;
logger.tracef('Iterator next contains %d entries', count);
if (count > 0) {
var projectionSize = DECODE_VINT(bytebuf); // projections size
logger.tracef('Projection size is %d', projectionSize);
if (!f.existy(projectionSize))
return {continue: false};
var entries = [], i = 0;
while (i++ < count) {
var meta = DECODE_UBYTE(bytebuf);
if (!f.existy(meta))
return {continue: false};
var hasMeta = meta == 1; // meta
var entry = {};
if (hasMeta) {
var flags = DECODE_UBYTE(bytebuf);
if (!f.existy(flags))
return {continue: false};
var lifespan = decodeTimestamp(flags, INFINITE_LIFESPAN, ['created', 'lifespan'], bytebuf);
if (!f.existy(lifespan))
return {continue: false};
var idle = decodeTimestamp(flags, INFINITE_MAXIDLE, ['lastUsed', 'maxIdle'], bytebuf);
if (!f.existy(idle))
return {continue: false};
var version = DECODE_VERSION(bytebuf);
entry = f.merge(lifespan, idle, version);
}
var kv = decodePairActions(decoderKey, decoderValue)(bytebuf); // key/value pair
if (f.existy(kv)) {
entries.push(f.merge(entry, kv));
} else {
return {continue: false};
}
}
return {result: entries, continue: true};
}
return {result: [], continue: true};
}
}
}
}());
var NoListenerInterestsMixin = (function() {
return {
encodeListenerInterests: function (opts) {
return [codec.encodeUByte(0x0F)]; // interested in all
}
}
}());
var ListenerInterestsMixin = (function() {
return {
encodeListenerInterests: function (opts) {
return [codec.encodeUByte(0x0F)]; // interested in all
}
}
}());
var MediaType = function(mediaType) {
var mediaCodecs = resolveEncoders(mediaType);
function resolveEncoders(mediaType) {
return f.dispatch(
f.isa('application/json', encoder(2, encoderJson, decoderJson()))
, f.isa('text/plain', encoder(13, encoderString, decoderString()))
)(mediaType);
}
function encoderJson(json) {
return codec.encodeJSON(json);
}
function decoderJson() {
return codec.decodeJSON();
}
function encoderString(str) {
return codec.encodeString(str);
}
function decoderString() {
return codec.decodeString();
}
function encoder(id, mediaEncoder, mediaDecoder) {
return function() {
return [
codec.encodeUByte(id)
, mediaEncoder
, mediaDecoder
];
}
}
return {
getMediaType: function() {
return mediaType;
},
encodeMediaType: function() {
return mediaCodecs[0];
},
encodeMedia: function(obj) {
return mediaCodecs[1](obj);
},
decodeMedia: function() {
return mediaCodecs[2];
},
};
};
var NoMediaTypesMixin = (function() {
return {
init: function(clientOpts) {
this.keyMediaType = new MediaType('text/plain');
this.valueMediaType = new MediaType('text/plain');
},
encodeMediaTypes: function () {
return []; // no media types
},
encodeMediaKey: function(k) {
return codec.encodeString(k);
},
encodeMediaValue: function(v) {
return codec.encodeString(v);
}
}
}());
var MediaTypesMixin = (function() {
function encodeMediaType(mediaType) {
return [
codec.encodeUByte(1)
, mediaType.encodeMediaType()
, codec.encodeUByte(0)
];
}
function decodeServerMediaType(bytebuf) {
var mediaType = DECODE_UBYTE(bytebuf);
if (!f.existy(mediaType))
return {continue: false};
if (_.isEqual(mediaType, 1)) {
var decodePredefinedMediaType =
f.actions([codec.decodeUByte(), codec.decodeVInt()], codec.allDecoded(2));
var predefinedMediaType = decodePredefinedMediaType(bytebuf);
if (!f.existy(predefinedMediaType))
return {continue: false};
}
return {continue: true};
}
return {
init: function(clientOpts) {
logger.debugf("Before init, key media type was: %s"
, f.existy(this.keyMediaType) ? this.keyMediaType.getMediaType() : 'undefined'
);
this.keyMediaType = new MediaType(clientOpts.dataFormat.keyType);
this.valueMediaType = new MediaType(clientOpts.dataFormat.valueType);
this.authOpts = clientOpts.authentication;
logger.debugf("After init, key media type is: %s"
, f.existy(this.keyMediaType) ? this.keyMediaType.getMediaType() : 'undefined'
);
},
encodeMediaTypes: function() {
var encodedKeyMediaType = encodeMediaType(this.keyMediaType);
var encodedValueMediaType = encodeMediaType(this.valueMediaType);
return f.cat(encodedKeyMediaType, encodedValueMediaType);
},
encodeMediaKey: function(k) {
return this.keyMediaType.encodeMedia(k);
},
encodeMediaValue: function(v) {
return this.valueMediaType.encodeMedia(v);
},
getKeyMediaType: function() {
return this.keyMediaType.getMediaType();
},
getValueMediaType: function() {
return this.valueMediaType.getMediaType();
},
decodeServerMediaTypes: function(header, bytebuf) {
var keyMediaType = decodeServerMediaType(bytebuf);
if (keyMediaType.continue) {
var valueMediaType = decodeServerMediaType(bytebuf);
if (valueMediaType.continue)
return {result: undefined, continue: true};
}
return {continue: false};
}
}
}());
var SASLMixin = (function() {
var logger = u.logger('sasl');
return {
decodeAuthMech: function(header, bytebuf) {
return {result: undefined, continue: true};
},
sasl: function(authOpts) {
logger.debugf(authOpts);
return function () {
var response;
switch (authOpts.saslMechanism) {
case 'PLAIN':
logger.tracef('Try auth with PLAIN');
response = '';
response += '\0';
response += authOpts.userName;
response += '\0';
response += authOpts.password;
return [codec.encodeString('PLAIN'), codec.encodeString(response)];
case 'EXTERNAL':
logger.tracef('Try auth with EXTERNAL');
response = '';
response += authOpts.userName;
return [codec.encodeString('EXTERNAL'), codec.encodeString(response)];
default:
throw new Error('Unsupported auth mechanism ' + authOpts.saslMechanism);
}
}
},
decodeSasl: function(header, bytebuf) {
var authDone = DECODE_UBYTE(bytebuf);
if(authDone == 1) {
var result = DECODE_UBYTE(bytebuf);
return {result: undefined, continue: true};
}
// continue for the next challenge
return {result: undefined, continue: false};
}
}
}());
function Protocol(v, clientOpts) {
this.version = v;
this.clientOpts = clientOpts;
this.init(clientOpts);
}
Protocol.prototype.init = _.identity;
var Protocol25 = function(clientOpts) {
Protocol.call(this, 25, clientOpts);
};
var Protocol22 = function(clientOpts) {
Protocol.call(this, 22, clientOpts);
};
var Protocol29 = function(clientOpts) {
Protocol.call(this, 29, clientOpts);
};
// TODO: Missing operations, just for reference
var IdsMixin = {
queryId: function() { return 0x1F },
authMechId: function() { return 0x21 },
authReqId: function() { return 0x23 }
};
_.extend(Protocol22.prototype
, EncodeMixin
, ExpiryEncodeMixin
, DecodeMixin
, IdsMixin
, ListenersMixin
, NoListenerInterestsMixin
, NoMediaTypesMixin
);
_.extend(Protocol25.prototype
, EncodeMixin
, ExpiryEncodeMixin
, DecodeMixin
, IdsMixin
, ListenersMixin
, NoListenerInterestsMixin
, IteratorMixin
, NoMediaTypesMixin
);
_.extend(Protocol29.prototype
, EncodeMixin
, ExpiryEncodeMixin
, DecodeMixin
, IdsMixin
, ListenersMixin
, ListenerInterestsMixin
, IteratorMixin
, MediaTypesMixin
, SASLMixin
// TODO 2.6 new ops: getStream and putStream
// TODO 2.6 add listener change: listener event interests
// TODO 2.7 new ops: prepare, commit and rollback
// TODO 2.7 new ops: counter operations
// TODO 2.7 new events: counter events
// TODO 2.8 listener events: can come from any connection
// TODO 2.8 header change: media types
);
exports.version22 = function(clientOpts) {
return new Protocol22(clientOpts);
};
exports.version25 = function(clientOpts) {
return new Protocol25(clientOpts);
};
exports.version29 = function(clientOpts) {
return new Protocol29(clientOpts);
};
}.call(this));