四好公路
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

1323 lines
39 KiB

'use strict';
var Binary = require('binary');
var Buffermaker = require('buffermaker');
var _ = require('lodash');
var crc32 = require('buffer-crc32');
var protocol = require('./protocol_struct');
var getCodec = require('../codec');
var REQUEST_TYPE = protocol.REQUEST_TYPE;
var ERROR_CODE = protocol.ERROR_CODE;
var GROUP_ERROR = protocol.GROUP_ERROR;
var PartitionMetadata = protocol.PartitionMetadata;
const API_KEY_TO_NAME = _.invert(REQUEST_TYPE);
const MessageSizeTooLarge = require('../errors/MessageSizeTooLargeError');
var API_VERSION = 0;
var REPLICA_ID = -1;
var GROUPS_PROTOCOL_TYPE = 'consumer';
function groupByTopic (payloads) {
return payloads.reduce(function (out, p) {
out[p.topic] = out[p.topic] || {};
out[p.topic][p.partition] = p;
return out;
}, {});
}
function encodeRequestWithLength (request) {
return new Buffermaker().Int32BE(request.length).string(request).make();
}
function encodeRequestHeader (clientId, correlationId, apiKey, apiVersion) {
return new Buffermaker()
.Int16BE(apiKey)
.Int16BE(apiVersion || API_VERSION)
.Int32BE(correlationId)
.Int16BE(clientId.length)
.string(clientId);
}
function encodeFetchRequest (maxWaitMs, minBytes) {
return function encodeFetchRequest (clientId, correlationId, payloads) {
return _encodeFetchRequest(clientId, correlationId, payloads, maxWaitMs, minBytes);
};
}
function encodeFetchRequestV1 (maxWaitMs, minBytes) {
return function encodeFetchRequest (clientId, correlationId, payloads) {
return _encodeFetchRequest(clientId, correlationId, payloads, maxWaitMs, minBytes, 1);
};
}
function encodeFetchRequestV2 (maxWaitMs, minBytes) {
return function encodeFetchRequest (clientId, correlationId, payloads) {
return _encodeFetchRequest(clientId, correlationId, payloads, maxWaitMs, minBytes, 2);
};
}
function decodeTopics (decodePartitions) {
return function (end, vars) {
if (--vars.topicNum === 0) end();
this.word16bs('topic')
.tap(function (vars) {
this.buffer('topic', vars.topic);
vars.topic = vars.topic.toString();
})
.word32bs('partitionNum')
.loop(decodePartitions);
};
}
function _encodeFetchRequest (clientId, correlationId, payloads, maxWaitMs, minBytes, version) {
payloads = groupByTopic(payloads);
var request = encodeRequestHeader(clientId, correlationId, REQUEST_TYPE.fetch, version);
var topics = Object.keys(payloads);
request.Int32BE(REPLICA_ID).Int32BE(maxWaitMs).Int32BE(minBytes).Int32BE(topics.length);
topics.forEach(function (topic) {
request.Int16BE(topic.length).string(topic);
var partitions = _.toPairs(payloads[topic]).map(function (pairs) {
return pairs[1];
});
request.Int32BE(partitions.length);
partitions.forEach(function (p) {
request.Int32BE(p.partition).Int64BE(p.offset).Int32BE(p.maxBytes);
});
});
return encodeRequestWithLength(request.make());
}
function decodeFetchResponse (cb, maxTickMessages) {
return function (resp) {
return _decodeFetchResponse(resp, cb, maxTickMessages, 0);
};
}
function decodeFetchResponseV1 (cb, maxTickMessages) {
return function (resp) {
return _decodeFetchResponse(resp, cb, maxTickMessages, 1);
};
}
function createGroupError (errorCode) {
if (errorCode == null || errorCode === 0) {
return null;
}
var error = ERROR_CODE[errorCode];
if (error in GROUP_ERROR) {
error = new GROUP_ERROR[error]('Kafka Error Code: ' + errorCode);
} else {
error = new Error(error);
}
error.errorCode = errorCode;
return error;
}
function _decodeFetchResponse (resp, cb, maxTickMessages, version) {
var topics = {};
Binary.parse(resp)
.word32bs('size')
.word32bs('correlationId')
.tap(function () {
if (version >= 1) {
this.word32bs('throttleTime');
}
})
.word32bs('topicNum')
.loop(decodeTopics(decodePartitions));
function decodePartitions (end, vars) {
if (--vars.partitionNum === 0) end();
topics[vars.topic] = topics[vars.topic] || {};
this.word32bs('partition')
.word16bs('errorCode')
.word64bs('highWaterOffset')
.word32bs('messageSetSize')
.tap(function (vars) {
this.buffer('messageSet', vars.messageSetSize);
if (vars.errorCode !== 0) {
// eslint-disable-next-line standard/no-callback-literal
return cb({ topic: vars.topic, partition: vars.partition, message: ERROR_CODE[vars.errorCode] });
}
var messageSet = decodeMessageSet(
vars.topic,
vars.partition,
vars.messageSet,
cb,
maxTickMessages,
vars.highWaterOffset
);
if (messageSet.length) {
var offset = messageSet[messageSet.length - 1];
topics[vars.topic][vars.partition] = offset;
topics[vars.topic].highWaterOffset = vars.highWaterOffset;
}
});
}
cb && cb(null, 'done', topics);
}
function decodeMessageSet (topic, partition, messageSet, cb, maxTickMessages, highWaterOffset) {
var set = [];
var messageCount = 0;
const messageSetSize = messageSet.length;
while (messageSet.length > 0) {
var cur = 8 + 4 + 4 + 1 + 1 + 4 + 4;
Binary.parse(messageSet)
.word64bs('offset')
.word32bs('messageSize')
.tap(function (vars) {
if (vars.messageSize > messageSet.length - 12) {
vars.partial = true;
}
})
.word32bs('crc')
.word8bs('magicByte')
.word8bs('attributes')
.tap(function (vars) {
if (vars.magicByte > 0) {
this.word64bs('timestamp');
cur += 8;
}
})
.word32bs('key')
.tap(function (vars) {
if (vars.key === -1) {
vars.key = null;
return;
}
cur += vars.key;
this.buffer('key', vars.key);
})
.word32bs('value')
.tap(function (vars) {
if (vars.value !== -1) {
cur += vars.value;
this.buffer('value', vars.value);
} else {
vars.value = null;
}
if (vars.attributes === 0 && vars.messageSize > messageSetSize) {
cb(
new MessageSizeTooLarge({
topic: topic,
offset: vars.offset,
partition: partition
})
);
return;
}
if (!vars.partial && vars.offset !== null) {
messageCount++;
set.push(vars.offset);
if (!cb) return;
var codec = getCodec(vars.attributes);
if (!codec) {
const message = {
topic: topic,
value: vars.value,
offset: vars.offset,
partition: partition,
highWaterOffset: highWaterOffset,
key: vars.key
};
if (vars.timestamp) {
message.timestamp = new Date(vars.timestamp);
}
return cb(null, 'message', message);
}
codec.decode(vars.value, function (error, inlineMessageSet) {
if (error) return; // Not sure where to report this
decodeMessageSet(topic, partition, inlineMessageSet, cb, maxTickMessages, highWaterOffset);
});
}
});
// Defensive code around potential denial of service
if (maxTickMessages && messageCount > maxTickMessages) break;
messageSet = messageSet.slice(cur);
}
return set;
}
function encodeMetadataRequest (clientId, correlationId, topics) {
var request = encodeRequestHeader(clientId, correlationId, REQUEST_TYPE.metadata);
request.Int32BE(topics.length);
topics.forEach(function (topic) {
request.Int16BE(topic.length).string(topic);
});
return encodeRequestWithLength(request.make());
}
function decodeMetadataResponse (resp) {
var brokers = {};
var out = {};
var topics = {};
var errors = [];
Binary.parse(resp)
.word32bs('size')
.word32bs('correlationId')
.word32bs('brokerNum')
.loop(decodeBrokers)
.word32bs('topicNum')
.loop(_decodeTopics);
function decodeBrokers (end, vars) {
if (vars.brokerNum-- === 0) return end();
this.word32bs('nodeId')
.word16bs('host')
.tap(function (vars) {
this.buffer('host', vars.host);
vars.host = vars.host.toString();
})
.word32bs('port')
.tap(function (vars) {
brokers[vars.nodeId] = { nodeId: vars.nodeId, host: vars.host, port: vars.port };
});
}
function _decodeTopics (end, vars) {
if (vars.topicNum-- === 0) return end();
this.word16bs('topicError')
.word16bs('topic')
.tap(function (vars) {
this.buffer('topic', vars.topic);
vars.topic = vars.topic.toString();
})
.word32bs('partitionNum')
.tap(function (vars) {
if (vars.topicError !== 0) {
return errors.push(ERROR_CODE[vars.topicError]);
}
this.loop(decodePartitions);
});
}
function decodePartitions (end, vars) {
if (vars.partitionNum-- === 0) return end();
topics[vars.topic] = topics[vars.topic] || {};
this.word16bs('errorCode')
.word32bs('partition')
.word32bs('leader')
.word32bs('replicasNum')
.tap(function (vars) {
var buffer = this.buffer('replicas', vars.replicasNum * 4).vars.replicas;
this.vars.replicas = bufferToArray(vars.replicasNum, buffer);
})
.word32bs('isrNum')
.tap(function (vars) {
var buffer = this.buffer('isr', vars.isrNum * 4).vars.isr;
this.vars.isr = bufferToArray(vars.isrNum, buffer);
if (vars.errorCode === 0 || vars.errorCode === 9) {
topics[vars.topic][vars.partition] = new PartitionMetadata(
vars.topic,
vars.partition,
vars.leader,
vars.replicas,
vars.isr
);
} else {
errors.push(ERROR_CODE[vars.errorCode]);
}
});
}
if (!_.isEmpty(errors)) out.error = errors;
out.metadata = topics;
return [brokers, out];
}
function bufferToArray (num, buffer) {
var ret = [];
for (var i = 0; i < num; i++) {
ret.push(Binary.parse(buffer).word32bs('r').vars.r);
buffer = buffer.slice(4);
}
return ret;
}
function encodeOffsetCommitRequest (group) {
return function (clientId, correlationId, payloads) {
return _encodeOffsetCommitRequest(clientId, correlationId, group, payloads);
};
}
function encodeOffsetCommitV2Request (clientId, correlationId, group, generationId, memberId, payloads) {
return encodeOffsetCommitGenericRequest(clientId, correlationId, group, generationId, memberId, payloads, 2);
}
function encodeOffsetCommitV1Request (clientId, correlationId, group, generationId, memberId, payloads) {
return encodeOffsetCommitGenericRequest(clientId, correlationId, group, generationId, memberId, payloads, 1);
}
function encodeOffsetCommitGenericRequest (
clientId,
correlationId,
group,
generationId,
memberId,
payloads,
apiVersion
) {
payloads = groupByTopic(payloads);
var request = encodeRequestHeader(clientId, correlationId, REQUEST_TYPE.offsetCommit, apiVersion);
var topics = Object.keys(payloads);
request
.Int16BE(group.length)
.string(group)
.Int32BE(generationId)
.Int16BE(memberId.length)
.string(memberId)
.Int64BE(-1)
.Int32BE(topics.length);
topics.forEach(function (topic) {
request.Int16BE(topic.length).string(topic);
var partitions = _.toPairs(payloads[topic]).map(function (pairs) {
return pairs[1];
});
request.Int32BE(partitions.length);
partitions.forEach(function (p) {
request.Int32BE(p.partition).Int64BE(p.offset).Int16BE(p.metadata.length).string(p.metadata);
});
});
return encodeRequestWithLength(request.make());
}
function _encodeOffsetCommitRequest (clientId, correlationId, group, payloads) {
payloads = groupByTopic(payloads);
var request = encodeRequestHeader(clientId, correlationId, REQUEST_TYPE.offsetCommit);
var topics = Object.keys(payloads);
request.Int16BE(group.length).string(group).Int32BE(topics.length);
topics.forEach(function (topic) {
request.Int16BE(topic.length).string(topic);
var partitions = _.toPairs(payloads[topic]).map(function (pairs) {
return pairs[1];
});
request.Int32BE(partitions.length);
partitions.forEach(function (p) {
request.Int32BE(p.partition).Int64BE(p.offset).Int16BE(p.metadata.length).string(p.metadata);
});
});
return encodeRequestWithLength(request.make());
}
function decodeOffsetCommitResponse (resp) {
var topics = {};
Binary.parse(resp)
.word32bs('size')
.word32bs('correlationId')
.word32bs('topicNum')
.loop(decodeTopics(decodePartitions));
function decodePartitions (end, vars) {
if (--vars.partitionNum === 0) end();
topics[vars.topic] = topics[vars.topic] || {};
this.word32bs('partition').word16bs('errorcode').tap(function (vars) {
topics[vars.topic]['partition'] = vars.partition;
topics[vars.topic]['errorCode'] = vars.errorcode;
});
}
return topics;
}
function encodeProduceRequest (requireAcks, ackTimeoutMs) {
return function (clientId, correlationId, payloads) {
return _encodeProduceRequest(clientId, correlationId, payloads, requireAcks, ackTimeoutMs, 0);
};
}
function encodeProduceV1Request (requireAcks, ackTimeoutMs) {
return function (clientId, correlationId, payloads) {
return _encodeProduceRequest(clientId, correlationId, payloads, requireAcks, ackTimeoutMs, 1);
};
}
function encodeProduceV2Request (requireAcks, ackTimeoutMs) {
return function (clientId, correlationId, payloads) {
return _encodeProduceRequest(clientId, correlationId, payloads, requireAcks, ackTimeoutMs, 2);
};
}
function _encodeProduceRequest (clientId, correlationId, payloads, requireAcks, ackTimeoutMs, apiVersion) {
payloads = groupByTopic(payloads);
var request = encodeRequestHeader(clientId, correlationId, REQUEST_TYPE.produce, apiVersion);
var topics = Object.keys(payloads);
request.Int16BE(requireAcks).Int32BE(ackTimeoutMs).Int32BE(topics.length);
topics.forEach(function (topic) {
request.Int16BE(topic.length).string(topic);
var reqs = _.toPairs(payloads[topic]).map(function (pairs) {
return pairs[1];
});
request.Int32BE(reqs.length);
reqs.forEach(function (p) {
var messageSet = encodeMessageSet(p.messages, apiVersion === 2 ? 1 : 0);
request.Int32BE(p.partition).Int32BE(messageSet.length).string(messageSet);
});
});
return encodeRequestWithLength(request.make());
}
function encodeMessageSet (messageSet, magic) {
var buffer = new Buffermaker();
messageSet.forEach(function (message) {
var msg = encodeMessage(message, magic);
buffer.Int64BE(0).Int32BE(msg.length).string(msg);
});
return buffer.make();
}
function encodeMessage (message, magic) {
if (magic == null) {
magic = 0;
}
var m = new Buffermaker().Int8(magic).Int8(message.attributes);
// Add timestamp support for new messages
if (magic === 1) {
m.Int64BE(message.timestamp);
}
var key = message.key;
setValueOnBuffer(m, key);
var value = message.value;
setValueOnBuffer(m, value);
m = m.make();
var crc = crc32.signed(m);
return new Buffermaker().Int32BE(crc).string(m).make();
}
function setValueOnBuffer (buffer, value) {
if (value != null) {
if (Buffer.isBuffer(value)) {
buffer.Int32BE(value.length);
} else {
if (typeof value !== 'string') value = value.toString();
buffer.Int32BE(Buffer.byteLength(value));
}
buffer.string(value);
} else {
buffer.Int32BE(-1);
}
}
function decodeProduceV1Response (resp) {
var topics = {};
var error;
Binary.parse(resp)
.word32bs('size')
.word32bs('correlationId')
.word32bs('topicNum')
.loop(decodeTopics(decodePartitions))
.word32bs('throttleTime');
function decodePartitions (end, vars) {
if (--vars.partitionNum === 0) end();
topics[vars.topic] = topics[vars.topic] || {};
this.word32bs('partition').word16bs('errorCode').word64bs('offset').tap(function (vars) {
if (vars.errorCode) {
error = new Error(ERROR_CODE[vars.errorCode]);
} else {
topics[vars.topic][vars.partition] = vars.offset;
}
});
}
return error || topics;
}
function decodeProduceV2Response (resp) {
var topics = {};
var error;
Binary.parse(resp)
.word32bs('size')
.word32bs('correlationId')
.word32bs('topicNum')
.loop(decodeTopics(decodePartitions))
.word32bs('throttleTime');
function decodePartitions (end, vars) {
if (--vars.partitionNum === 0) end();
topics[vars.topic] = topics[vars.topic] || {};
this.word32bs('partition').word16bs('errorCode').word64bs('offset').word64bs('timestamp').tap(function (vars) {
if (vars.errorCode) {
error = new Error(ERROR_CODE[vars.errorCode]);
} else {
topics[vars.topic][vars.partition] = vars.offset;
}
});
}
return error || topics;
}
function decodeProduceResponse (resp) {
var topics = {};
var error;
Binary.parse(resp)
.word32bs('size')
.word32bs('correlationId')
.word32bs('topicNum')
.loop(decodeTopics(decodePartitions));
function decodePartitions (end, vars) {
if (--vars.partitionNum === 0) end();
topics[vars.topic] = topics[vars.topic] || {};
this.word32bs('partition').word16bs('errorCode').word64bs('offset').tap(function (vars) {
if (vars.errorCode) {
error = new Error(ERROR_CODE[vars.errorCode]);
} else {
topics[vars.topic][vars.partition] = vars.offset;
}
});
}
return error || topics;
}
function encodeOffsetFetchRequest (group) {
return function (clientId, correlationId, payloads) {
return _encodeOffsetFetchRequest(clientId, correlationId, group, payloads);
};
}
function encodeOffsetFetchV1Request (clientId, correlationId, group, payloads) {
var request = encodeRequestHeader(clientId, correlationId, REQUEST_TYPE.offsetFetch, 1);
var topics = Object.keys(payloads);
request.Int16BE(group.length).string(group).Int32BE(topics.length);
topics.forEach(function (topic) {
request.Int16BE(topic.length).string(topic).Int32BE(payloads[topic].length);
payloads[topic].forEach(function (p) {
request.Int32BE(p);
});
});
return encodeRequestWithLength(request.make());
}
function _encodeOffsetFetchRequest (clientId, correlationId, group, payloads) {
payloads = groupByTopic(payloads);
var request = encodeRequestHeader(clientId, correlationId, REQUEST_TYPE.offsetFetch);
var topics = Object.keys(payloads);
request.Int16BE(group.length).string(group).Int32BE(topics.length);
topics.forEach(function (topic) {
request.Int16BE(topic.length).string(topic);
var partitions = _.toPairs(payloads[topic]).map(function (pairs) {
return pairs[1];
});
request.Int32BE(partitions.length);
partitions.forEach(function (p) {
request.Int32BE(p.partition);
});
});
return encodeRequestWithLength(request.make());
}
function decodeOffsetFetchResponse (resp) {
var topics = {};
Binary.parse(resp)
.word32bs('size')
.word32bs('correlationId')
.word32bs('topicNum')
.loop(decodeTopics(decodePartitions));
function decodePartitions (end, vars) {
if (--vars.partitionNum === 0) end();
topics[vars.topic] = topics[vars.topic] || {};
this.word32bs('partition')
.word64bs('offset')
.word16bs('metadata')
.tap(function (vars) {
if (vars.metadata === -1) {
return;
}
this.buffer('metadata', vars.metadata);
})
.word16bs('errorCode')
.tap(function (vars) {
topics[vars.topic][vars.partition] = vars.errorCode === 0 ? vars.offset : -1;
});
}
return topics;
}
function decodeOffsetFetchV1Response (resp) {
var topics = {};
Binary.parse(resp)
.word32bs('size')
.word32bs('correlationId')
.word32bs('topicNum')
.loop(decodeTopics(decodePartitions));
function decodePartitions (end, vars) {
if (--vars.partitionNum === 0) end();
topics[vars.topic] = topics[vars.topic] || {};
this.word32bs('partition')
.word64bs('offset')
.word16bs('metadata')
.tap(function (vars) {
if (vars.metadata === -1) {
return;
}
this.buffer('metadata', vars.metadata);
})
.word16bs('errorCode')
.tap(function (vars) {
if (vars.metadata.length === 0 && vars.offset === 0) {
topics[vars.topic][vars.partition] = -1;
} else {
topics[vars.topic][vars.partition] = vars.errorCode === 0 ? vars.offset : -1;
}
});
}
return topics;
}
function encodeOffsetRequest (clientId, correlationId, payloads) {
payloads = groupByTopic(payloads);
var request = encodeRequestHeader(clientId, correlationId, REQUEST_TYPE.offset);
var topics = Object.keys(payloads);
request.Int32BE(REPLICA_ID).Int32BE(topics.length);
topics.forEach(function (topic) {
request.Int16BE(topic.length).string(topic);
var partitions = _.toPairs(payloads[topic]).map(function (pairs) {
return pairs[1];
});
request.Int32BE(partitions.length);
partitions.forEach(function (p) {
request.Int32BE(p.partition).Int64BE(p.time).Int32BE(p.maxNum);
});
});
return encodeRequestWithLength(request.make());
}
function decodeOffsetResponse (resp) {
var topics = {};
Binary.parse(resp)
.word32bs('size')
.word32bs('correlationId')
.word32bs('topicNum')
.loop(decodeTopics(decodePartitions));
function decodePartitions (end, vars) {
if (--vars.partitionNum === 0) end();
topics[vars.topic] = topics[vars.topic] || {};
this.word32bs('partition').word16bs('errorCode').word32bs('offsetNum').loop(decodeOffsets);
}
function decodeOffsets (end, vars) {
if (--vars.offsetNum <= 0) end();
topics[vars.topic][vars.partition] = topics[vars.topic][vars.partition] || [];
this.word64bs('offset').tap(function (vars) {
if (vars.offset != null) topics[vars.topic][vars.partition].push(vars.offset);
});
}
return topics;
}
function encodeGroupCoordinatorRequest (clientId, correlationId, groupId) {
var request = encodeRequestHeader(clientId, correlationId, REQUEST_TYPE.groupCoordinator);
request.Int16BE(groupId.length).string(groupId);
return encodeRequestWithLength(request.make());
}
function encodeGroupHeartbeatRequest (clientId, correlationId, groupId, generationId, memberId) {
var request = encodeRequestHeader(clientId, correlationId, REQUEST_TYPE.heartbeat);
request.Int16BE(groupId.length).string(groupId).Int32BE(generationId).Int16BE(memberId.length).string(memberId);
return encodeRequestWithLength(request.make());
}
function decodeGroupHeartbeatResponse (resp) {
var result = null;
Binary.parse(resp).word32bs('size').word32bs('correlationId').word16bs('errorCode').tap(function (vars) {
result = createGroupError(vars.errorCode);
});
return result;
}
function decodeGroupCoordinatorResponse (resp) {
var result;
Binary.parse(resp)
.word32bs('size')
.word32bs('correlationId')
.word16bs('errorCode')
.word32bs('coordinatorId')
.word16bs('coordinatorHost')
.tap(function (vars) {
this.buffer('coordinatorHost', vars.coordinatorHost);
vars.coordinatorHost = vars.coordinatorHost.toString();
})
.word32bs('coordinatorPort')
.tap(function (vars) {
if (vars.errorCode !== 0) {
result = createGroupError(vars.errorCode);
return;
}
result = {
coordinatorHost: vars.coordinatorHost,
coordinatorPort: vars.coordinatorPort,
coordinatorId: vars.coordinatorId
};
});
return result;
}
/*
ProtocolType => "consumer"
ProtocolName => AssignmentStrategy
AssignmentStrategy => string
ProtocolMetadata => Version Subscription UserData
Version => int16
Subscription => [Topic]
Topic => string
UserData => bytes
*/
function encodeGroupProtocol (protocol) {
this.Int16BE(protocol.name.length).string(protocol.name).string(_encodeProtocolData(protocol));
}
function _encodeProtocolData (protocol) {
var protocolByte = new Buffermaker().Int16BE(protocol.version).Int32BE(protocol.subscription.length);
protocol.subscription.forEach(function (topic) {
protocolByte.Int16BE(topic.length).string(topic);
});
if (protocol.userData) {
var userDataStr = JSON.stringify(protocol.userData);
var data = Buffer.from(userDataStr, 'utf8');
protocolByte.Int32BE(data.length).string(data);
} else {
protocolByte.Int32BE(-1);
}
return encodeRequestWithLength(protocolByte.make());
}
function decodeSyncGroupResponse (resp) {
var result;
Binary.parse(resp)
.word32bs('size')
.word32bs('correlationId')
.word16bs('errorCode')
.tap(function (vars) {
result = createGroupError(vars.errorCode);
})
.word32bs('memberAssignment')
.tap(function (vars) {
if (result) {
return;
}
this.buffer('memberAssignment', vars.memberAssignment);
result = decodeMemberAssignment(vars.memberAssignment);
});
return result;
}
/*
MemberAssignment => Version PartitionAssignment
Version => int16
PartitionAssignment => [Topic [Partition]]
Topic => string
Partition => int32
UserData => bytes
*/
function decodeMemberAssignment (assignmentBytes) {
var assignment = {
partitions: {}
};
Binary.parse(assignmentBytes)
.word16bs('version')
.tap(function (vars) {
assignment.version = vars.version;
})
.word32bs('partitionAssignment')
.loop(function (end, vars) {
if (vars.partitionAssignment-- === 0) return end();
var topic;
var partitions = [];
this.word16bs('topic')
.tap(function (vars) {
this.buffer('topic', vars.topic);
topic = vars.topic.toString();
})
.word32bs('partitionsNum')
.loop(function (end, vars) {
if (vars.partitionsNum-- === 0) return end();
this.word32bs('partition').tap(function (vars) {
partitions.push(vars.partition);
});
});
assignment.partitions[topic] = partitions;
})
.word32bs('userData')
.tap(function (vars) {
if (vars.userData == null || vars.userData === -1) {
return;
}
this.buffer('userData', vars.userData);
try {
assignment.userData = JSON.parse(vars.userData.toString());
} catch (e) {
assignment.userData = 'JSON Parse error';
}
});
return assignment;
}
function encodeSyncGroupRequest (clientId, correlationId, groupId, generationId, memberId, groupAssignment) {
var request = encodeRequestHeader(clientId, correlationId, REQUEST_TYPE.syncGroup);
request.Int16BE(groupId.length).string(groupId).Int32BE(generationId).Int16BE(memberId.length).string(memberId);
if (groupAssignment && groupAssignment.length) {
request.Int32BE(groupAssignment.length);
groupAssignment.forEach(function (assignment) {
request
.Int16BE(assignment.memberId.length)
.string(assignment.memberId)
.string(_encodeMemberAssignment(assignment));
});
} else {
request.Int32BE(0);
}
return encodeRequestWithLength(request.make());
}
function _encodeMemberAssignment (assignment) {
var numberOfTopics = Object.keys(assignment.topicPartitions).length;
var assignmentByte = new Buffermaker().Int16BE(assignment.version).Int32BE(numberOfTopics);
for (var tp in assignment.topicPartitions) {
if (!assignment.topicPartitions.hasOwnProperty(tp)) {
continue;
}
var partitions = assignment.topicPartitions[tp];
assignmentByte.Int16BE(tp.length).string(tp).Int32BE(partitions.length);
partitions.forEach(function (partition) {
assignmentByte.Int32BE(partition);
});
}
if (assignment.userData) {
var userDataStr = JSON.stringify(assignment.userData);
var data = Buffer.from(userDataStr, 'utf8');
assignmentByte.Int32BE(data.length).string(data);
} else {
assignmentByte.Int32BE(-1);
}
return encodeRequestWithLength(assignmentByte.make());
}
function encodeLeaveGroupRequest (clientId, correlationId, groupId, memberId) {
var request = encodeRequestHeader(clientId, correlationId, REQUEST_TYPE.leaveGroup);
request.Int16BE(groupId.length).string(groupId).Int16BE(memberId.length).string(memberId);
return encodeRequestWithLength(request.make());
}
function decodeLeaveGroupResponse (resp) {
var error = null;
Binary.parse(resp).word32bs('size').word32bs('correlationId').word16bs('errorCode').tap(function (vars) {
error = createGroupError(vars.errorCode);
});
return error;
}
// {
// name: '', // string
// subscription: [/* topics */],
// version: 0, // integer
// userData: {} //arbitary
// }
/*
JoinGroupRequest => GroupId SessionTimeout MemberId ProtocolType GroupProtocols
GroupId => string
SessionTimeout => int32
MemberId => string
ProtocolType => string
GroupProtocols => [ProtocolName ProtocolMetadata]
ProtocolName => string
ProtocolMetadata => bytes
*/
function encodeJoinGroupRequest (clientId, correlationId, groupId, memberId, sessionTimeout, groupProtocols) {
var request = encodeRequestHeader(clientId, correlationId, REQUEST_TYPE.joinGroup);
request
.Int16BE(groupId.length)
.string(groupId)
.Int32BE(sessionTimeout)
.Int16BE(memberId.length)
.string(memberId)
.Int16BE(GROUPS_PROTOCOL_TYPE.length)
.string(GROUPS_PROTOCOL_TYPE)
.Int32BE(groupProtocols.length);
groupProtocols.forEach(encodeGroupProtocol.bind(request));
return encodeRequestWithLength(request.make());
}
/*
v0 and v1 supported in 0.9.0 and greater
JoinGroupResponse => ErrorCode GenerationId GroupProtocol LeaderId MemberId Members
ErrorCode => int16
GenerationId => int32
GroupProtocol => string
LeaderId => string
MemberId => string
Members => [MemberId MemberMetadata]
MemberId => string
MemberMetadata => bytes
*/
function decodeJoinGroupResponse (resp) {
var result = {
members: []
};
var error;
Binary.parse(resp)
.word32bs('size')
.word32bs('correlationId')
.word16bs('errorCode')
.tap(function (vars) {
error = createGroupError(vars.errorCode);
})
.word32bs('generationId')
.tap(function (vars) {
result.generationId = vars.generationId;
})
.word16bs('groupProtocol')
.tap(function (vars) {
this.buffer('groupProtocol', vars.groupProtocol);
result.groupProtocol = vars.groupProtocol = vars.groupProtocol.toString();
})
.word16bs('leaderId')
.tap(function (vars) {
this.buffer('leaderId', vars.leaderId);
result.leaderId = vars.leaderId = vars.leaderId.toString();
})
.word16bs('memberId')
.tap(function (vars) {
this.buffer('memberId', vars.memberId);
result.memberId = vars.memberId = vars.memberId.toString();
})
.word32bs('memberNum')
.loop(function (end, vars) {
if (error) {
return end();
}
if (vars.memberNum-- === 0) return end();
var memberMetadata;
this.word16bs('groupMemberId')
.tap(function (vars) {
this.buffer('groupMemberId', vars.groupMemberId);
vars.memberId = vars.groupMemberId.toString();
})
.word32bs('memberMetadata')
.tap(function (vars) {
if (vars.memberMetadata > -1) {
this.buffer('memberMetadata', vars.memberMetadata);
memberMetadata = decodeGroupData(this.vars.memberMetadata);
memberMetadata.id = vars.memberId;
result.members.push(memberMetadata);
}
});
});
return error || result;
}
function decodeGroupData (resp) {
var topics = [];
var parsed = Binary.parse(resp)
.word16bs('version')
.word32bs('subscriptionNum')
.loop(function decodeSubscription (end, vars) {
if (vars.subscriptionNum-- === 0) return end();
this.word16bs('topic').tap(function () {
this.buffer('topic', vars.topic);
topics.push(vars.topic.toString());
});
})
.word32bs('userData')
.tap(function (vars) {
if (vars.userData === -1) {
vars.userData = undefined;
return;
}
this.buffer('userData', vars.userData);
try {
vars.userData = JSON.parse(vars.userData.toString());
} catch (error) {
vars.userData = 'JSON parse error';
}
}).vars;
return {
subscription: topics,
version: parsed.version,
userData: parsed.userData
};
}
function encodeDescribeGroups (clientId, correlationId, groups) {
const request = encodeRequestHeader(clientId, correlationId, REQUEST_TYPE.describeGroups);
request.Int32BE(groups.length);
groups.forEach(groupId => {
request.Int16BE(groupId.length).string(groupId);
});
return encodeRequestWithLength(request.make());
}
function decodeDescribeGroups (resp) {
let results = {};
Binary.parse(resp).word32bs('size').word32bs('correlationId').word32bs('describeNum').loop(decodeDescriptions);
function decodeDescriptions (end, vars) {
if (vars.describeNum-- === 0) return end();
let described = { members: [] };
this.word16bs('errorCode')
.tap(vars => {
described.error = createGroupError(vars.errorCode);
})
.word16bs('groupId')
.tap(vars => {
this.buffer('groupId', vars.groupId);
described.groupId = vars.groupId.toString();
})
.word16bs('state')
.tap(vars => {
this.buffer('state', vars.state);
described.state = vars.state.toString();
})
.word16bs('protocolType')
.tap(vars => {
this.buffer('protocolType', vars.protocolType);
described.protocolType = vars.protocolType.toString();
})
.word16bs('protocol')
.tap(vars => {
this.buffer('protocol', vars.protocol);
described.protocol = vars.protocol.toString();
// keep this for error cases
results[described.groupId] = described;
})
.word32bs('membersNum')
.loop(function decodeGroupMembers (end, vars) {
if (vars.membersNum-- === 0) return end();
let member = {};
this.word16bs('memberId')
.tap(vars => {
this.buffer('memberId', vars.memberId);
member.memberId = vars.memberId.toString();
})
.word16bs('clientId')
.tap(vars => {
this.buffer('clientId', vars.clientId);
member.clientId = vars.clientId.toString();
})
.word16bs('clientHost')
.tap(vars => {
this.buffer('clientHost', vars.clientHost);
member.clientHost = vars.clientHost.toString();
})
.word32bs('memberMetadata')
.tap(vars => {
if (vars.memberMetadata > -1) {
this.buffer('memberMetadata', vars.memberMetadata);
let memberMetadata = decodeGroupData(vars.memberMetadata);
memberMetadata.id = member.memberId;
member.memberMetadata = memberMetadata;
}
})
.word32bs('memberAssignment')
.tap(vars => {
this.buffer('memberAssignment', vars.memberAssignment);
member.memberAssignment = decodeMemberAssignment(vars.memberAssignment);
described.members.push(member);
results[described.groupId] = described;
});
});
}
return results;
}
function encodeListGroups (clientId, correlationId) {
return encodeRequestWithLength(encodeRequestHeader(clientId, correlationId, REQUEST_TYPE.listGroups).make());
}
function decodeListGroups (resp) {
let groups = {};
Binary.parse(resp)
.word32bs('size')
.word32bs('correlationId')
.word16bs('errorCode')
.tap(vars => {
groups.error = createGroupError(vars.errorCode);
})
.word32bs('groupNum')
.loop(function (end, vars) {
if (vars.groupNum-- === 0) return end();
this.word16bs('groupId')
.tap(function (vars) {
this.buffer('groupId', vars.groupId);
vars.groupId = vars.groupId.toString();
})
.word16bs('protocolType')
.tap(function (vars) {
this.buffer('protocolType', vars.protocolType);
groups[vars.groupId] = vars.protocolType.toString();
});
});
return groups;
}
function encodeVersionsRequest (clientId, correlationId) {
return encodeRequestWithLength(encodeRequestHeader(clientId, correlationId, REQUEST_TYPE.apiVersions).make());
}
function decodeVersionsResponse (resp) {
const MAX_SUPPORT_VERSION = require('./protocolVersions').maxSupport;
const versions = Object.create(null);
let error = null;
Binary.parse(resp)
.word32bs('size')
.word32bs('correlationId')
.word16bs('errorCode')
.tap(function (vars) {
error = createGroupError(vars.errorCode);
})
.word32bs('apiNum')
.loop(function (end, vars) {
if (vars.apiNum-- === 0 || error) return end();
let apiKey, minVersion, maxVersion;
this.word16bs('apiKey')
.tap(vars => {
apiKey = vars.apiKey;
})
.word16bs('minVersion')
.tap(vars => {
minVersion = vars.minVersion;
})
.word16bs('maxVersion')
.tap(vars => {
maxVersion = vars.maxVersion;
});
const apiName = apiKey in API_KEY_TO_NAME ? API_KEY_TO_NAME[apiKey] : apiKey;
versions[apiName] = {
min: minVersion,
max: maxVersion,
usable: MAX_SUPPORT_VERSION[apiName] != null ? Math.min(MAX_SUPPORT_VERSION[apiName], maxVersion) : false
};
});
return error || versions;
}
exports.encodeFetchRequest = encodeFetchRequest;
exports.decodeFetchResponse = decodeFetchResponse;
exports.encodeFetchRequestV1 = encodeFetchRequestV1;
exports.decodeFetchResponseV1 = decodeFetchResponseV1;
exports.encodeFetchRequestV2 = encodeFetchRequestV2;
exports.encodeOffsetCommitRequest = encodeOffsetCommitRequest;
exports.encodeOffsetCommitV1Request = encodeOffsetCommitV1Request;
exports.encodeOffsetCommitV2Request = encodeOffsetCommitV2Request;
exports.decodeOffsetCommitResponse = decodeOffsetCommitResponse;
exports.encodeOffsetFetchRequest = encodeOffsetFetchRequest;
exports.encodeOffsetFetchV1Request = encodeOffsetFetchV1Request;
exports.decodeOffsetFetchResponse = decodeOffsetFetchResponse;
exports.decodeOffsetFetchV1Response = decodeOffsetFetchV1Response;
exports.encodeMetadataRequest = encodeMetadataRequest;
exports.decodeMetadataResponse = decodeMetadataResponse;
exports.encodeProduceRequest = encodeProduceRequest;
exports.encodeProduceV1Request = encodeProduceV1Request;
exports.encodeProduceV2Request = encodeProduceV2Request;
exports.decodeProduceResponse = decodeProduceResponse;
exports.decodeProduceV1Response = decodeProduceV1Response;
exports.decodeProduceV2Response = decodeProduceV2Response;
exports.encodeOffsetRequest = encodeOffsetRequest;
exports.decodeOffsetResponse = decodeOffsetResponse;
exports.encodeMessageSet = encodeMessageSet;
exports.encodeJoinGroupRequest = encodeJoinGroupRequest;
exports.decodeJoinGroupResponse = decodeJoinGroupResponse;
exports.encodeGroupCoordinatorRequest = encodeGroupCoordinatorRequest;
exports.decodeGroupCoordinatorResponse = decodeGroupCoordinatorResponse;
exports.encodeGroupHeartbeatRequest = encodeGroupHeartbeatRequest;
exports.decodeGroupHeartbeatResponse = decodeGroupHeartbeatResponse;
exports.encodeSyncGroupRequest = encodeSyncGroupRequest;
exports.decodeSyncGroupResponse = decodeSyncGroupResponse;
exports.encodeLeaveGroupRequest = encodeLeaveGroupRequest;
exports.decodeLeaveGroupResponse = decodeLeaveGroupResponse;
exports.encodeDescribeGroups = encodeDescribeGroups;
exports.decodeDescribeGroups = decodeDescribeGroups;
exports.encodeListGroups = encodeListGroups;
exports.decodeListGroups = decodeListGroups;
exports.encodeVersionsRequest = encodeVersionsRequest;
exports.decodeVersionsResponse = decodeVersionsResponse;