UNPKG

@jonaskello-forks/amqp-client

Version:

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

805 lines 24.2 kB
import { AMQPError } from './amqp-error.js'; import { AMQPView } from './amqp-view.js'; import { AMQPQueue } from './amqp-queue.js'; import { AMQPConsumer } from './amqp-consumer.js'; export class AMQPChannel { constructor(connection, id) { this.consumers = new Map(); this.promises = []; this.unconfirmedPublishes = []; this.closed = false; this.confirmId = 0; this.connection = connection; this.id = id; this.buffer = new AMQPView(new ArrayBuffer(connection.frameMax)); } queue(name = "", { passive = false, durable = name !== "", autoDelete = name === "", exclusive = name === "" } = {}, args = {}) { return new Promise((resolve, reject) => { this.queueDeclare(name, { passive, durable, autoDelete, exclusive }, args) .then(({ name }) => resolve(new AMQPQueue(this, name))) .catch(reject); }); } prefetch(prefetchCount) { return this.basicQos(prefetchCount); } onReturn(message) { console.error("Message returned from server", message); } close(reason = "", code = 200) { if (this.closed) return this.rejectClosed(); this.closed = true; let j = 0; const frame = this.buffer; frame.setUint8(j, 1); j += 1; frame.setUint16(j, this.id); j += 2; frame.setUint32(j, 0); j += 4; frame.setUint16(j, 20); j += 2; frame.setUint16(j, 40); j += 2; frame.setUint16(j, code); j += 2; j += frame.setShortString(j, reason); frame.setUint16(j, 0); j += 2; frame.setUint16(j, 0); j += 2; frame.setUint8(j, 206); j += 1; frame.setUint32(3, j - 8); return this.sendRpc(frame, j); } basicGet(queue, { noAck = true } = {}) { if (this.closed) return this.rejectClosed(); let j = 0; const frame = this.buffer; frame.setUint8(j, 1); j += 1; frame.setUint16(j, this.id); j += 2; frame.setUint32(j, 11); j += 4; frame.setUint16(j, 60); j += 2; frame.setUint16(j, 70); j += 2; frame.setUint16(j, 0); j += 2; j += frame.setShortString(j, queue); frame.setUint8(j, noAck ? 1 : 0); j += 1; frame.setUint8(j, 206); j += 1; frame.setUint32(3, j - 8); return this.sendRpc(frame, j); } basicConsume(queue, { tag = "", noAck = true, exclusive = false, args = {} } = {}, callback) { if (this.closed) return this.rejectClosed(); let j = 0; const noWait = false; const noLocal = false; const frame = this.buffer; frame.setUint8(j, 1); j += 1; frame.setUint16(j, this.id); j += 2; frame.setUint32(j, 0); j += 4; frame.setUint16(j, 60); j += 2; frame.setUint16(j, 20); j += 2; frame.setUint16(j, 0); j += 2; j += frame.setShortString(j, queue); j += frame.setShortString(j, 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.setUint8(j, bits); j += 1; j += frame.setTable(j, args); frame.setUint8(j, 206); j += 1; frame.setUint32(3, j - 8); return new Promise((resolve, reject) => { this.sendRpc(frame, j).then((consumerTag) => { const consumer = new AMQPConsumer(this, consumerTag, callback); this.consumers.set(consumerTag, consumer); resolve(consumer); }).catch(reject); }); } basicCancel(tag) { if (this.closed) return this.rejectClosed(); const noWait = false; let j = 0; const frame = this.buffer; frame.setUint8(j, 1); j += 1; frame.setUint16(j, this.id); j += 2; frame.setUint32(j, 0); j += 4; frame.setUint16(j, 60); j += 2; frame.setUint16(j, 30); j += 2; j += frame.setShortString(j, tag); frame.setUint8(j, noWait ? 1 : 0); j += 1; frame.setUint8(j, 206); j += 1; frame.setUint32(3, j - 8); return new Promise((resolve, reject) => { this.sendRpc(frame, j).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(); let j = 0; const frame = this.buffer; frame.setUint8(j, 1); j += 1; frame.setUint16(j, this.id); j += 2; frame.setUint32(j, 13); j += 4; frame.setUint16(j, 60); j += 2; frame.setUint16(j, 80); j += 2; frame.setUint64(j, deliveryTag); j += 8; frame.setUint8(j, multiple ? 1 : 0); j += 1; frame.setUint8(j, 206); j += 1; return this.connection.send(new Uint8Array(frame.buffer, 0, 21)); } basicNack(deliveryTag, requeue = false, multiple = false) { if (this.closed) return this.rejectClosed(); let j = 0; const frame = this.buffer; frame.setUint8(j, 1); j += 1; frame.setUint16(j, this.id); j += 2; frame.setUint32(j, 13); j += 4; frame.setUint16(j, 60); j += 2; frame.setUint16(j, 120); j += 2; frame.setUint64(j, deliveryTag); j += 8; let bits = 0; if (multiple) bits = bits | (1 << 0); if (requeue) bits = bits | (1 << 1); frame.setUint8(j, bits); j += 1; frame.setUint8(j, 206); j += 1; return this.connection.send(new Uint8Array(frame.buffer, 0, 21)); } basicReject(deliveryTag, requeue = false) { if (this.closed) return this.rejectClosed(); let j = 0; const frame = this.buffer; frame.setUint8(j, 1); j += 1; frame.setUint16(j, this.id); j += 2; frame.setUint32(j, 13); j += 4; frame.setUint16(j, 60); j += 2; frame.setUint16(j, 90); j += 2; frame.setUint64(j, deliveryTag); j += 8; frame.setUint8(j, requeue ? 1 : 0); j += 1; frame.setUint8(j, 206); j += 1; return this.connection.send(new Uint8Array(frame.buffer, 0, 21)); } basicRecover(requeue = false) { if (this.closed) return this.rejectClosed(); let j = 0; const frame = this.buffer; frame.setUint8(j, 1); j += 1; frame.setUint16(j, this.id); j += 2; frame.setUint32(j, 5); j += 4; frame.setUint16(j, 60); j += 2; frame.setUint16(j, 110); j += 2; frame.setUint8(j, requeue ? 1 : 0); j += 1; frame.setUint8(j, 206); j += 1; return this.sendRpc(frame, j); } async basicPublish(exchange, routingKey, data, properties = {}, mandatory = false, immediate = false) { if (this.closed) return this.rejectClosed(); if (this.connection.blocked) return Promise.reject(new 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 = AMQPChannel.textEncoder.encode(data); } else { throw new TypeError(`Invalid type ${typeof data} for parameter data`); } let j = 0; const buffer = this.buffer; buffer.setUint8(j, 1); j += 1; buffer.setUint16(j, this.id); j += 2; j += 4; buffer.setUint16(j, 60); j += 2; buffer.setUint16(j, 40); 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, 206); j += 1; buffer.setUint32(3, j - 8); const headerStart = j; buffer.setUint8(j, 2); j += 1; buffer.setUint16(j, this.id); j += 2; j += 4; buffer.setUint16(j, 60); 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, 206); j += 1; buffer.setUint32(headerStart + 3, j - headerStart - 8); if (body.byteLength === 0) { await this.connection.send(new Uint8Array(buffer.buffer, 0, j)); } else if (j >= buffer.byteLength - 8) { await this.connection.send(new Uint8Array(buffer.buffer, 0, j)); j = 0; } for (let bodyPos = 0; bodyPos < body.byteLength;) { const frameSize = Math.min(body.byteLength - bodyPos, buffer.byteLength - 8 - j); const dataSlice = body.subarray(bodyPos, bodyPos + frameSize); buffer.setUint8(j, 3); j += 1; buffer.setUint16(j, this.id); j += 2; buffer.setUint32(j, frameSize); j += 4; const bodyView = new Uint8Array(buffer.buffer, j, frameSize); bodyView.set(dataSlice); j += frameSize; buffer.setUint8(j, 206); j += 1; await this.connection.send(new Uint8Array(buffer.buffer, 0, j)); bodyPos += frameSize; j = 0; } if (this.confirmId) { return new Promise((resolve, reject) => this.unconfirmedPublishes.push([this.confirmId++, resolve, reject])); } else { return Promise.resolve(0); } } basicQos(prefetchCount, prefetchSize = 0, global = false) { if (this.closed) return this.rejectClosed(); let j = 0; const frame = this.buffer; frame.setUint8(j, 1); j += 1; frame.setUint16(j, this.id); j += 2; frame.setUint32(j, 11); j += 4; frame.setUint16(j, 60); j += 2; frame.setUint16(j, 10); j += 2; frame.setUint32(j, prefetchSize); j += 4; frame.setUint16(j, prefetchCount); j += 2; frame.setUint8(j, global ? 1 : 0); j += 1; frame.setUint8(j, 206); j += 1; return this.sendRpc(frame, j); } basicFlow(active = true) { if (this.closed) return this.rejectClosed(); let j = 0; const frame = this.buffer; frame.setUint8(j, 1); j += 1; frame.setUint16(j, this.id); j += 2; frame.setUint32(j, 5); j += 4; frame.setUint16(j, 20); j += 2; frame.setUint16(j, 20); j += 2; frame.setUint8(j, active ? 1 : 0); j += 1; frame.setUint8(j, 206); j += 1; return this.sendRpc(frame, j); } confirmSelect() { if (this.closed) return this.rejectClosed(); let j = 0; const frame = this.buffer; frame.setUint8(j, 1); j += 1; frame.setUint16(j, this.id); j += 2; frame.setUint32(j, 5); j += 4; frame.setUint16(j, 85); j += 2; frame.setUint16(j, 10); j += 2; frame.setUint8(j, 0); j += 1; frame.setUint8(j, 206); j += 1; return this.sendRpc(frame, j); } queueDeclare(name = "", { passive = false, durable = name !== "", autoDelete = name === "", exclusive = name === "" } = {}, args = {}) { if (this.closed) return this.rejectClosed(); const noWait = false; let j = 0; const declare = this.buffer; declare.setUint8(j, 1); j += 1; declare.setUint16(j, this.id); j += 2; declare.setUint32(j, 0); j += 4; declare.setUint16(j, 50); j += 2; declare.setUint16(j, 10); j += 2; declare.setUint16(j, 0); j += 2; j += declare.setShortString(j, 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.setUint8(j, bits); j += 1; j += declare.setTable(j, args); declare.setUint8(j, 206); j += 1; declare.setUint32(3, j - 8); return this.sendRpc(declare, j); } queueDelete(name = "", { ifUnused = false, ifEmpty = false } = {}) { if (this.closed) return this.rejectClosed(); const noWait = false; let j = 0; const frame = this.buffer; frame.setUint8(j, 1); j += 1; frame.setUint16(j, this.id); j += 2; frame.setUint32(j, 0); j += 4; frame.setUint16(j, 50); j += 2; frame.setUint16(j, 40); j += 2; frame.setUint16(j, 0); j += 2; j += frame.setShortString(j, name); let bits = 0; if (ifUnused) bits = bits | (1 << 0); if (ifEmpty) bits = bits | (1 << 1); if (noWait) bits = bits | (1 << 2); frame.setUint8(j, bits); j += 1; frame.setUint8(j, 206); j += 1; frame.setUint32(3, j - 8); return this.sendRpc(frame, j); } queueBind(queue, exchange, routingKey, args = {}) { if (this.closed) return this.rejectClosed(); const noWait = false; let j = 0; const bind = this.buffer; bind.setUint8(j, 1); j += 1; bind.setUint16(j, this.id); j += 2; bind.setUint32(j, 0); j += 4; bind.setUint16(j, 50); j += 2; bind.setUint16(j, 20); j += 2; bind.setUint16(j, 0); j += 2; j += bind.setShortString(j, queue); j += bind.setShortString(j, exchange); j += bind.setShortString(j, routingKey); bind.setUint8(j, noWait ? 1 : 0); j += 1; j += bind.setTable(j, args); bind.setUint8(j, 206); j += 1; bind.setUint32(3, j - 8); return this.sendRpc(bind, j); } queueUnbind(queue, exchange, routingKey, args = {}) { if (this.closed) return this.rejectClosed(); let j = 0; const unbind = this.buffer; unbind.setUint8(j, 1); j += 1; unbind.setUint16(j, this.id); j += 2; unbind.setUint32(j, 0); j += 4; unbind.setUint16(j, 50); j += 2; unbind.setUint16(j, 50); j += 2; unbind.setUint16(j, 0); j += 2; j += unbind.setShortString(j, queue); j += unbind.setShortString(j, exchange); j += unbind.setShortString(j, routingKey); j += unbind.setTable(j, args); unbind.setUint8(j, 206); j += 1; unbind.setUint32(3, j - 8); return this.sendRpc(unbind, j); } queuePurge(queue) { if (this.closed) return this.rejectClosed(); const noWait = false; let j = 0; const purge = this.buffer; purge.setUint8(j, 1); j += 1; purge.setUint16(j, this.id); j += 2; purge.setUint32(j, 0); j += 4; purge.setUint16(j, 50); j += 2; purge.setUint16(j, 30); j += 2; purge.setUint16(j, 0); j += 2; j += purge.setShortString(j, queue); purge.setUint8(j, noWait ? 1 : 0); j += 1; purge.setUint8(j, 206); j += 1; purge.setUint32(3, j - 8); return this.sendRpc(purge, j); } exchangeDeclare(name, type, { passive = false, durable = true, autoDelete = false, internal = false } = {}, args = {}) { const noWait = false; let j = 0; const frame = this.buffer; frame.setUint8(j, 1); j += 1; frame.setUint16(j, this.id); j += 2; frame.setUint32(j, 0); j += 4; frame.setUint16(j, 40); j += 2; frame.setUint16(j, 10); j += 2; frame.setUint16(j, 0); j += 2; j += frame.setShortString(j, name); j += frame.setShortString(j, 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.setUint8(j, bits); j += 1; j += frame.setTable(j, args); frame.setUint8(j, 206); j += 1; frame.setUint32(3, j - 8); return this.sendRpc(frame, j); } exchangeDelete(name, { ifUnused = false } = {}) { const noWait = false; let j = 0; const frame = this.buffer; frame.setUint8(j, 1); j += 1; frame.setUint16(j, this.id); j += 2; frame.setUint32(j, 0); j += 4; frame.setUint16(j, 40); j += 2; frame.setUint16(j, 20); j += 2; frame.setUint16(j, 0); j += 2; j += frame.setShortString(j, name); let bits = 0; if (ifUnused) bits = bits | (1 << 0); if (noWait) bits = bits | (1 << 1); frame.setUint8(j, bits); j += 1; frame.setUint8(j, 206); j += 1; frame.setUint32(3, j - 8); return this.sendRpc(frame, j); } exchangeBind(destination, source, routingKey = "", args = {}) { if (this.closed) return this.rejectClosed(); let j = 0; const bind = this.buffer; bind.setUint8(j, 1); j += 1; bind.setUint16(j, this.id); j += 2; bind.setUint32(j, 0); j += 4; bind.setUint16(j, 40); j += 2; bind.setUint16(j, 30); j += 2; bind.setUint16(j, 0); j += 2; j += bind.setShortString(j, destination); j += bind.setShortString(j, source); j += bind.setShortString(j, routingKey); bind.setUint8(j, 0); j += 1; j += bind.setTable(j, args); bind.setUint8(j, 206); j += 1; bind.setUint32(3, j - 8); return this.sendRpc(bind, j); } exchangeUnbind(destination, source, routingKey = "", args = {}) { if (this.closed) return this.rejectClosed(); let j = 0; const unbind = this.buffer; unbind.setUint8(j, 1); j += 1; unbind.setUint16(j, this.id); j += 2; unbind.setUint32(j, 0); j += 4; unbind.setUint16(j, 40); j += 2; unbind.setUint16(j, 40); j += 2; unbind.setUint16(j, 0); j += 2; j += unbind.setShortString(j, destination); j += unbind.setShortString(j, source); j += unbind.setShortString(j, routingKey); unbind.setUint8(j, 0); j += 1; j += unbind.setTable(j, args); unbind.setUint8(j, 206); j += 1; unbind.setUint32(3, j - 8); return this.sendRpc(unbind, j); } txSelect() { return this.txMethod(10); } txCommit() { return this.txMethod(20); } txRollback() { return this.txMethod(30); } txMethod(methodId) { if (this.closed) return this.rejectClosed(); let j = 0; const frame = this.buffer; frame.setUint8(j, 1); j += 1; frame.setUint16(j, this.id); j += 2; frame.setUint32(j, 4); j += 4; frame.setUint16(j, 90); j += 2; frame.setUint16(j, methodId); j += 2; frame.setUint8(j, 206); j += 1; return this.sendRpc(frame, j); } resolvePromise(value) { const promise = this.promises.shift(); if (promise) { const [resolve,] = promise; resolve(value); return true; } return false; } rejectPromise(err) { const promise = this.promises.shift(); if (promise) { const [, reject] = promise; reject(err); return true; } return false; } sendRpc(frame, frameSize) { return new Promise((resolve, reject) => { this.connection.send(new Uint8Array(frame.buffer, 0, frameSize)) .then(() => this.promises.push([resolve, reject])) .catch(reject); }); } setClosed(err) { if (!this.closed) { this.closed = true; this.consumers.forEach((consumer) => consumer.setClosed(err)); this.consumers.clear(); while (this.rejectPromise(err)) { 1; } this.unconfirmedPublishes.forEach(([, , reject]) => reject(err)); } } rejectClosed() { return Promise.reject(new 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 { console.warn("Cant find unconfirmed deliveryTag", deliveryTag, "multiple:", multiple, "nack:", nack); } } onMessageReady(message) { if (this.delivery) { delete this.delivery; this.deliver(message); } else if (this.getMessage) { delete this.getMessage; this.resolvePromise(message); } else { delete this.returned; this.onReturn(message); } } deliver(message) { queueMicrotask(() => { const consumer = this.consumers.get(message.consumerTag); if (consumer) { consumer.onMessage(message); } else { console.warn("Consumer", message.consumerTag, "not available on channel", this.id); } }); } } AMQPChannel.textEncoder = new TextEncoder(); //# sourceMappingURL=amqp-channel.js.map