@jonaskello-forks/amqp-client
Version:
AMQP 0-9-1 client, both for browsers (WebSocket) and node (TCP Socket)
805 lines • 24.2 kB
JavaScript
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