UNPKG

@cloudamqp/amqp-client

Version:

AMQP 0-9-1 client, both for browsers (WebSocket) and node (TCP Socket)

715 lines 25.1 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.AMQPChannel = void 0; const amqp_error_js_1 = require("./amqp-error.js"); const AMQPFrame = __importStar(require("./amqp-frame.js")); const amqp_view_js_1 = require("./amqp-view.js"); const amqp_consumer_js_1 = require("./amqp-consumer.js"); class AMQPChannel { constructor(connection, id) { this.consumers = new Map(); this.rpcQueue = Promise.resolve(true); this.rpcCallbacks = []; this.unconfirmedPublishes = []; this.closed = false; this.confirmId = 0; this.connection = connection; this.id = id; this.onerror = (reason) => { this.logger?.error(`channel ${this.id} closed: ${reason}`); this.connection.onerror(new amqp_error_js_1.AMQPError(`Channel ${this.id} closed: ${reason}`, this.connection)); }; } get logger() { return this.connection.logger; } open() { const channelOpen = new AMQPFrame.Writer({ bufferSize: 13, type: AMQPFrame.Type.METHOD, channel: this.id, frameSize: 5, classId: AMQPFrame.ClassId.CHANNEL, method: AMQPFrame.ChannelMethod.OPEN, }); channelOpen.writeUint8(0); channelOpen.finalize(); return this.sendRpc(channelOpen); } prefetch(prefetchCount) { return this.basicQos(prefetchCount); } onReturn(message) { this.logger?.error("Message returned from server", message); } close(reason = "", code = 200) { if (this.closed) return this.rejectClosed(); this.closed = true; const frame = new AMQPFrame.Writer({ bufferSize: 512, type: AMQPFrame.Type.METHOD, channel: this.id, classId: AMQPFrame.ClassId.CHANNEL, method: AMQPFrame.ChannelMethod.CLOSE, }); frame.writeUint16(code); frame.writeShortString(reason); frame.writeUint16(0); frame.writeUint16(0); frame.finalize(); return this.sendRpc(frame); } basicGet(queue, { noAck = true } = {}) { if (this.closed) return this.rejectClosed(); const frame = new AMQPFrame.Writer({ bufferSize: 512, type: AMQPFrame.Type.METHOD, channel: this.id, classId: AMQPFrame.ClassId.BASIC, method: AMQPFrame.BasicMethod.GET, }); frame.writeUint16(0); frame.writeShortString(queue); frame.writeUint8(noAck ? 1 : 0); frame.finalize(); return this.sendRpc(frame); } basicConsume(queue, { tag = "", noAck = true, exclusive = false, args = {} } = {}, callback) { if (this.closed) return this.rejectClosed(); const noWait = false; const noLocal = false; const frame = new AMQPFrame.Writer({ bufferSize: 4096, type: AMQPFrame.Type.METHOD, channel: this.id, classId: AMQPFrame.ClassId.BASIC, method: AMQPFrame.BasicMethod.CONSUME, }); frame.writeUint16(0); frame.writeShortString(queue); frame.writeShortString(tag); let bits = 0; if (noLocal) bits = bits | (1 << 0); if (noAck) bits = bits | (1 << 1); if (exclusive) bits = bits | (1 << 2); if (noWait) bits = bits | (1 << 3); frame.writeUint8(bits); frame.writeTable(args); frame.finalize(); return new Promise((resolve, reject) => { this.sendRpc(frame) .then((consumerTag) => { let consumer; if (callback) { consumer = new amqp_consumer_js_1.AMQPConsumer(this, consumerTag, callback); } else { consumer = new amqp_consumer_js_1.AMQPGeneratorConsumer(this, consumerTag); } this.consumers.set(consumerTag, consumer); resolve(consumer); }) .catch(reject); }); } basicCancel(tag) { if (this.closed) return this.rejectClosed(); const noWait = false; const frame = new AMQPFrame.Writer({ bufferSize: 512, type: AMQPFrame.Type.METHOD, channel: this.id, classId: AMQPFrame.ClassId.BASIC, method: AMQPFrame.BasicMethod.CANCEL, }); frame.writeShortString(tag); frame.writeUint8(noWait ? 1 : 0); frame.finalize(); return new Promise((resolve, reject) => { this.sendRpc(frame) .then((consumerTag) => { const consumer = this.consumers.get(consumerTag); if (consumer) { consumer.setClosed(); this.consumers.delete(consumerTag); } resolve(this); }) .catch(reject); }); } basicAck(deliveryTag, multiple = false) { if (this.closed) return this.rejectClosed(); const frame = new AMQPFrame.Writer({ bufferSize: 21, type: AMQPFrame.Type.METHOD, channel: this.id, frameSize: 13, classId: AMQPFrame.ClassId.BASIC, method: AMQPFrame.BasicMethod.ACK, }); frame.writeUint64(deliveryTag); frame.writeUint8(multiple ? 1 : 0); frame.finalize(); return this.connection.send(frame.toUint8Array()); } basicNack(deliveryTag, requeue = false, multiple = false) { if (this.closed) return this.rejectClosed(); const frame = new AMQPFrame.Writer({ bufferSize: 21, type: AMQPFrame.Type.METHOD, channel: this.id, frameSize: 13, classId: AMQPFrame.ClassId.BASIC, method: AMQPFrame.BasicMethod.NACK, }); frame.writeUint64(deliveryTag); let bits = 0; if (multiple) bits = bits | (1 << 0); if (requeue) bits = bits | (1 << 1); frame.writeUint8(bits); frame.finalize(); return this.connection.send(frame.toUint8Array()); } basicReject(deliveryTag, requeue = false) { if (this.closed) return this.rejectClosed(); const frame = new AMQPFrame.Writer({ bufferSize: 21, type: AMQPFrame.Type.METHOD, channel: this.id, frameSize: 13, classId: AMQPFrame.ClassId.BASIC, method: AMQPFrame.BasicMethod.REJECT, }); frame.writeUint64(deliveryTag); frame.writeUint8(requeue ? 1 : 0); frame.finalize(); return this.connection.send(frame.toUint8Array()); } basicRecover(requeue = false) { if (this.closed) return this.rejectClosed(); const frame = new AMQPFrame.Writer({ bufferSize: 13, type: AMQPFrame.Type.METHOD, channel: this.id, frameSize: 5, classId: AMQPFrame.ClassId.BASIC, method: AMQPFrame.BasicMethod.RECOVER, }); frame.writeUint8(requeue ? 1 : 0); frame.finalize(); return this.sendRpc(frame); } async basicPublish(exchange, routingKey, data, properties = {}, mandatory = false, immediate = false) { if (this.closed) return this.rejectClosed(); if (this.connection.blocked) return Promise.reject(new amqp_error_js_1.AMQPError(`Connection blocked by server: ${this.connection.blocked}`, this.connection)); let body; if (typeof Buffer !== "undefined" && data instanceof Buffer) { body = data; } else if (data instanceof Uint8Array) { body = data; } else if (data instanceof ArrayBuffer) { body = new Uint8Array(data); } else if (data === null) { body = new Uint8Array(0); } else if (typeof data === "string") { body = this.connection.textEncoder.encode(data); } else { throw new TypeError(`Invalid type ${typeof data} for parameter data`); } let j = 0; let buffer = this.connection.bufferPool.pop() || new amqp_view_js_1.AMQPView(new ArrayBuffer(this.connection.frameMax)); buffer.setUint8(j, AMQPFrame.Type.METHOD); j += 1; buffer.setUint16(j, this.id); j += 2; j += 4; buffer.setUint16(j, AMQPFrame.ClassId.BASIC); j += 2; buffer.setUint16(j, AMQPFrame.BasicMethod.PUBLISH); j += 2; buffer.setUint16(j, 0); j += 2; j += buffer.setShortString(j, exchange); j += buffer.setShortString(j, routingKey); let bits = 0; if (mandatory) bits = bits | (1 << 0); if (immediate) bits = bits | (1 << 1); buffer.setUint8(j, bits); j += 1; buffer.setUint8(j, AMQPFrame.End.CODE); j += 1; buffer.setUint32(3, j - 8); const headerStart = j; buffer.setUint8(j, AMQPFrame.Type.HEADER); j += 1; buffer.setUint16(j, this.id); j += 2; j += 4; buffer.setUint16(j, AMQPFrame.ClassId.BASIC); j += 2; buffer.setUint16(j, 0); j += 2; buffer.setUint32(j, 0); j += 4; buffer.setUint32(j, body.byteLength); j += 4; j += buffer.setProperties(j, properties); buffer.setUint8(j, AMQPFrame.End.CODE); j += 1; buffer.setUint32(headerStart + 3, j - headerStart - 8); let bufferView = new Uint8Array(buffer.buffer); const bodyFrameCount = Math.ceil(body.byteLength / (this.connection.frameMax - 8)); const bufferSize = j + body.byteLength + 8 * bodyFrameCount; if (buffer.byteLength < bufferSize) { const newBuffer = new ArrayBuffer(bufferSize); const newBufferView = new Uint8Array(newBuffer); newBufferView.set(bufferView.subarray(0, j)); buffer = new amqp_view_js_1.AMQPView(newBuffer); bufferView = newBufferView; } for (let bodyPos = 0; bodyPos < body.byteLength;) { const frameSize = Math.min(body.byteLength - bodyPos, this.connection.frameMax - 8); const dataSlice = body.subarray(bodyPos, bodyPos + frameSize); buffer.setUint8(j, AMQPFrame.Type.BODY); j += 1; buffer.setUint16(j, this.id); j += 2; buffer.setUint32(j, frameSize); j += 4; bufferView.set(dataSlice, j); j += frameSize; buffer.setUint8(j, AMQPFrame.End.CODE); j += 1; bodyPos += frameSize; } const sendFrames = this.connection.send(bufferView.subarray(0, j)); if (this.confirmId) { const wait4Confirm = new Promise((resolve, reject) => this.unconfirmedPublishes.push([this.confirmId++, resolve, reject])); return sendFrames.then(() => wait4Confirm).finally(() => this.connection.bufferPool.push(buffer)); } else { return sendFrames.then(() => 0).finally(() => this.connection.bufferPool.push(buffer)); } } basicQos(prefetchCount, prefetchSize = 0, global = false) { if (this.closed) return this.rejectClosed(); const frame = new AMQPFrame.Writer({ bufferSize: 19, type: AMQPFrame.Type.METHOD, channel: this.id, frameSize: 11, classId: AMQPFrame.ClassId.BASIC, method: AMQPFrame.BasicMethod.QOS, }); frame.writeUint32(prefetchSize); frame.writeUint16(prefetchCount); frame.writeUint8(global ? 1 : 0); frame.finalize(); return this.sendRpc(frame); } basicFlow(active = true) { if (this.closed) return this.rejectClosed(); const frame = new AMQPFrame.Writer({ bufferSize: 13, type: AMQPFrame.Type.METHOD, channel: this.id, frameSize: 5, classId: AMQPFrame.ClassId.CHANNEL, method: AMQPFrame.BasicMethod.CONSUME, }); frame.writeUint8(active ? 1 : 0); frame.finalize(); return this.sendRpc(frame); } confirmSelect() { if (this.closed) return this.rejectClosed(); const frame = new AMQPFrame.Writer({ bufferSize: 13, type: AMQPFrame.Type.METHOD, channel: this.id, frameSize: 5, classId: AMQPFrame.ClassId.CONFIRM, method: AMQPFrame.ConfirmMethod.SELECT, }); frame.writeUint8(0); frame.finalize(); return this.sendRpc(frame); } queueDeclare(name = "", { passive = false, durable = name !== "", autoDelete = name === "", exclusive = name === "" } = {}, args = {}) { if (this.closed) return this.rejectClosed(); const noWait = false; const declare = new AMQPFrame.Writer({ bufferSize: 4096, type: AMQPFrame.Type.METHOD, channel: this.id, classId: AMQPFrame.ClassId.QUEUE, method: AMQPFrame.QueueMethod.DECLARE, }); declare.writeUint16(0); declare.writeShortString(name); let bits = 0; if (passive) bits = bits | (1 << 0); if (durable) bits = bits | (1 << 1); if (exclusive) bits = bits | (1 << 2); if (autoDelete) bits = bits | (1 << 3); if (noWait) bits = bits | (1 << 4); declare.writeUint8(bits); declare.writeTable(args); declare.finalize(); return this.sendRpc(declare); } queueDelete(name = "", { ifUnused = false, ifEmpty = false } = {}) { if (this.closed) return this.rejectClosed(); const noWait = false; const frame = new AMQPFrame.Writer({ bufferSize: 512, type: AMQPFrame.Type.METHOD, channel: this.id, classId: AMQPFrame.ClassId.QUEUE, method: AMQPFrame.QueueMethod.DELETE, }); frame.writeUint16(0); frame.writeShortString(name); let bits = 0; if (ifUnused) bits = bits | (1 << 0); if (ifEmpty) bits = bits | (1 << 1); if (noWait) bits = bits | (1 << 2); frame.writeUint8(bits); frame.finalize(); return this.sendRpc(frame); } queueBind(queue, exchange, routingKey, args = {}) { if (this.closed) return this.rejectClosed(); const noWait = false; const bind = new AMQPFrame.Writer({ bufferSize: 4096, type: AMQPFrame.Type.METHOD, channel: this.id, classId: AMQPFrame.ClassId.QUEUE, method: AMQPFrame.BasicMethod.CONSUME, }); bind.writeUint16(0); bind.writeShortString(queue); bind.writeShortString(exchange); bind.writeShortString(routingKey); bind.writeUint8(noWait ? 1 : 0); bind.writeTable(args); bind.finalize(); return this.sendRpc(bind); } queueUnbind(queue, exchange, routingKey, args = {}) { if (this.closed) return this.rejectClosed(); const unbind = new AMQPFrame.Writer({ bufferSize: 4096, type: AMQPFrame.Type.METHOD, channel: this.id, classId: AMQPFrame.ClassId.QUEUE, method: AMQPFrame.QueueMethod.UNBIND, }); unbind.writeUint16(0); unbind.writeShortString(queue); unbind.writeShortString(exchange); unbind.writeShortString(routingKey); unbind.writeTable(args); unbind.finalize(); return this.sendRpc(unbind); } queuePurge(queue) { if (this.closed) return this.rejectClosed(); const noWait = false; const purge = new AMQPFrame.Writer({ bufferSize: 512, type: AMQPFrame.Type.METHOD, channel: this.id, classId: AMQPFrame.ClassId.QUEUE, method: AMQPFrame.BasicMethod.CANCEL, }); purge.writeUint16(0); purge.writeShortString(queue); purge.writeUint8(noWait ? 1 : 0); purge.finalize(); return this.sendRpc(purge); } exchangeDeclare(name, type, { passive = false, durable = true, autoDelete = false, internal = false } = {}, args = {}) { if (this.closed) return this.rejectClosed(); const noWait = false; const frame = new AMQPFrame.Writer({ bufferSize: 4096, type: AMQPFrame.Type.METHOD, channel: this.id, classId: AMQPFrame.ClassId.EXCHANGE, method: AMQPFrame.ExchangeMethod.DECLARE, }); frame.writeUint16(0); frame.writeShortString(name); frame.writeShortString(type); let bits = 0; if (passive) bits = bits | (1 << 0); if (durable) bits = bits | (1 << 1); if (autoDelete) bits = bits | (1 << 2); if (internal) bits = bits | (1 << 3); if (noWait) bits = bits | (1 << 4); frame.writeUint8(bits); frame.writeTable(args); frame.finalize(); return this.sendRpc(frame); } exchangeDelete(name, { ifUnused = false } = {}) { if (this.closed) return this.rejectClosed(); const noWait = false; const frame = new AMQPFrame.Writer({ bufferSize: 512, type: AMQPFrame.Type.METHOD, channel: this.id, classId: AMQPFrame.ClassId.EXCHANGE, method: AMQPFrame.BasicMethod.CONSUME, }); frame.writeUint16(0); frame.writeShortString(name); let bits = 0; if (ifUnused) bits = bits | (1 << 0); if (noWait) bits = bits | (1 << 1); frame.writeUint8(bits); frame.finalize(); return this.sendRpc(frame); } exchangeBind(destination, source, routingKey = "", args = {}) { if (this.closed) return this.rejectClosed(); const bind = new AMQPFrame.Writer({ bufferSize: 4096, type: AMQPFrame.Type.METHOD, channel: this.id, classId: AMQPFrame.ClassId.EXCHANGE, method: AMQPFrame.BasicMethod.CANCEL, }); bind.writeUint16(0); bind.writeShortString(destination); bind.writeShortString(source); bind.writeShortString(routingKey); bind.writeUint8(0); bind.writeTable(args); bind.finalize(); return this.sendRpc(bind); } exchangeUnbind(destination, source, routingKey = "", args = {}) { if (this.closed) return this.rejectClosed(); const unbind = new AMQPFrame.Writer({ bufferSize: 4096, type: AMQPFrame.Type.METHOD, channel: this.id, classId: AMQPFrame.ClassId.EXCHANGE, method: AMQPFrame.ExchangeMethod.UNBIND, }); unbind.writeUint16(0); unbind.writeShortString(destination); unbind.writeShortString(source); unbind.writeShortString(routingKey); unbind.writeUint8(0); unbind.writeTable(args); unbind.finalize(); return this.sendRpc(unbind); } txSelect() { return this.txMethod(10); } txCommit() { return this.txMethod(20); } txRollback() { return this.txMethod(30); } txMethod(methodId) { if (this.closed) return this.rejectClosed(); const frame = new AMQPFrame.Writer({ bufferSize: 12, type: AMQPFrame.Type.METHOD, channel: this.id, frameSize: 4, classId: AMQPFrame.ClassId.TX, method: methodId, }); frame.finalize(); return this.sendRpc(frame); } sendRpc(frame) { const bytes = frame.toUint8Array(); return new Promise((resolve, reject) => { this.rpcQueue = this.rpcQueue .then(() => { this.rpcCallbacks.push([resolve, reject]); return this.connection.send(bytes).catch((err) => { const callbacks = this.rpcCallbacks.pop(); if (callbacks) { callbacks[1](err); } else { reject(err); } }); }) .catch(reject); }); } setClosed(err) { const closedByServer = err !== undefined; err || (err = new Error("Connection closed by client")); if (!this.closed) { this.closed = true; this.consumers.forEach((consumer) => consumer.setClosed(closedByServer ? err : undefined)); this.consumers.clear(); this.rpcCallbacks.forEach(([, reject]) => reject(err)); this.rpcCallbacks.length = 0; this.unconfirmedPublishes.forEach(([, , reject]) => reject(err)); this.unconfirmedPublishes.length = 0; if (closedByServer) this.onerror(err.message); } } rejectClosed() { return Promise.reject(new amqp_error_js_1.AMQPError("Channel is closed", this.connection)); } publishConfirmed(deliveryTag, multiple, nack) { const idx = this.unconfirmedPublishes.findIndex(([tag]) => tag === deliveryTag); if (idx !== -1) { const confirmed = multiple ? this.unconfirmedPublishes.splice(0, idx + 1) : this.unconfirmedPublishes.splice(idx, 1); confirmed.forEach(([tag, resolve, reject]) => { if (nack) reject(new Error("Message rejected")); else resolve(tag); }); } else { this.logger?.warn("Cant find unconfirmed deliveryTag", deliveryTag, "multiple:", multiple, "nack:", nack); } } onMessageReady(message) { message.body = message._rawBytes; if (this.delivery) { delete this.delivery; this.deliver(message); } else if (this.getMessage) { delete this.getMessage; this.resolveRPC(message); } else { delete this.returned; this.onReturn(message); } } resolveRPC(value) { const callbacks = this.rpcCallbacks.shift(); if (callbacks) { callbacks[0](value); } return value; } rejectRPC(err) { const callbacks = this.rpcCallbacks.shift(); if (callbacks) { callbacks[1](err); } return err; } deliver(message) { queueMicrotask(() => { const consumer = this.consumers.get(message.consumerTag); if (consumer) { consumer.onMessage(message); } else { this.logger?.warn("Consumer", message.consumerTag, "not available on channel", this.id); } }); } } exports.AMQPChannel = AMQPChannel; //# sourceMappingURL=amqp-channel.js.map