nats
Version:
Node.js client for NATS, a lightweight, high-performance cloud native messaging system
956 lines • 33.1 kB
JavaScript
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __asyncValues = (this && this.__asyncValues) || function (o) {
if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
var m = o[Symbol.asyncIterator], i;
return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i);
function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; }
function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.ProtocolHandler = exports.Subscriptions = exports.SubscriptionImpl = exports.Connect = exports.INFO = void 0;
/*
* Copyright 2018-2023 The NATS Authors
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
const encoders_1 = require("./encoders");
const transport_1 = require("./transport");
const util_1 = require("./util");
const databuffer_1 = require("./databuffer");
const servers_1 = require("./servers");
const queued_iterator_1 = require("./queued_iterator");
const muxsubscription_1 = require("./muxsubscription");
const heartbeats_1 = require("./heartbeats");
const parser_1 = require("./parser");
const msg_1 = require("./msg");
const semver_1 = require("./semver");
const core_1 = require("./core");
const options_1 = require("./options");
const FLUSH_THRESHOLD = 1024 * 32;
exports.INFO = /^INFO\s+([^\r\n]+)\r\n/i;
const PONG_CMD = (0, encoders_1.encode)("PONG\r\n");
const PING_CMD = (0, encoders_1.encode)("PING\r\n");
class Connect {
constructor(transport, opts, nonce) {
this.protocol = 1;
this.version = transport.version;
this.lang = transport.lang;
this.echo = opts.noEcho ? false : undefined;
this.verbose = opts.verbose;
this.pedantic = opts.pedantic;
this.tls_required = opts.tls ? true : undefined;
this.name = opts.name;
const creds = (opts && typeof opts.authenticator === "function"
? opts.authenticator(nonce)
: {}) || {};
(0, util_1.extend)(this, creds);
}
}
exports.Connect = Connect;
class SubscriptionImpl extends queued_iterator_1.QueuedIteratorImpl {
constructor(protocol, subject, opts = {}) {
var _a;
super();
(0, util_1.extend)(this, opts);
this.protocol = protocol;
this.subject = subject;
this.draining = false;
this.noIterator = typeof opts.callback === "function";
this.closed = (0, util_1.deferred)();
const asyncTraces = !(((_a = protocol.options) === null || _a === void 0 ? void 0 : _a.noAsyncTraces) || false);
if (opts.timeout) {
this.timer = (0, util_1.timeout)(opts.timeout, asyncTraces);
this.timer
.then(() => {
// timer was cancelled
this.timer = undefined;
})
.catch((err) => {
// timer fired
this.stop(err);
if (this.noIterator) {
this.callback(err, {});
}
});
}
if (!this.noIterator) {
// cleanup - they used break or return from the iterator
// make sure we clean up, if they didn't call unsub
this.iterClosed.then(() => {
this.closed.resolve();
this.unsubscribe();
});
}
}
setPrePostHandlers(opts) {
if (this.noIterator) {
const uc = this.callback;
const ingestion = opts.ingestionFilterFn
? opts.ingestionFilterFn
: () => {
return { ingest: true, protocol: false };
};
const filter = opts.protocolFilterFn ? opts.protocolFilterFn : () => {
return true;
};
const dispatched = opts.dispatchedFn ? opts.dispatchedFn : () => { };
this.callback = (err, msg) => {
const { ingest } = ingestion(msg);
if (!ingest) {
return;
}
if (filter(msg)) {
uc(err, msg);
dispatched(msg);
}
};
}
else {
this.protocolFilterFn = opts.protocolFilterFn;
this.dispatchedFn = opts.dispatchedFn;
}
}
callback(err, msg) {
this.cancelTimeout();
err ? this.stop(err) : this.push(msg);
}
close() {
if (!this.isClosed()) {
this.cancelTimeout();
const fn = () => {
this.stop();
if (this.cleanupFn) {
try {
this.cleanupFn(this, this.info);
}
catch (_err) {
// ignoring
}
}
this.closed.resolve();
};
if (this.noIterator) {
fn();
}
else {
//@ts-ignore: schedule the close once all messages are processed
this.push(fn);
}
}
}
unsubscribe(max) {
this.protocol.unsubscribe(this, max);
}
cancelTimeout() {
if (this.timer) {
this.timer.cancel();
this.timer = undefined;
}
}
drain() {
if (this.protocol.isClosed()) {
return Promise.reject(core_1.NatsError.errorForCode(core_1.ErrorCode.ConnectionClosed));
}
if (this.isClosed()) {
return Promise.reject(core_1.NatsError.errorForCode(core_1.ErrorCode.SubClosed));
}
if (!this.drained) {
this.draining = true;
this.protocol.unsub(this);
this.drained = this.protocol.flush((0, util_1.deferred)())
.then(() => {
this.protocol.subscriptions.cancel(this);
})
.catch(() => {
this.protocol.subscriptions.cancel(this);
});
}
return this.drained;
}
isDraining() {
return this.draining;
}
isClosed() {
return this.done;
}
getSubject() {
return this.subject;
}
getMax() {
return this.max;
}
getID() {
return this.sid;
}
}
exports.SubscriptionImpl = SubscriptionImpl;
class Subscriptions {
constructor() {
this.sidCounter = 0;
this.mux = null;
this.subs = new Map();
}
size() {
return this.subs.size;
}
add(s) {
this.sidCounter++;
s.sid = this.sidCounter;
this.subs.set(s.sid, s);
return s;
}
setMux(s) {
this.mux = s;
return s;
}
getMux() {
return this.mux;
}
get(sid) {
return this.subs.get(sid);
}
resub(s) {
this.sidCounter++;
this.subs.delete(s.sid);
s.sid = this.sidCounter;
this.subs.set(s.sid, s);
return s;
}
all() {
return Array.from(this.subs.values());
}
cancel(s) {
if (s) {
s.close();
this.subs.delete(s.sid);
}
}
handleError(err) {
if (err && err.permissionContext) {
const ctx = err.permissionContext;
const subs = this.all();
let sub;
if (ctx.operation === "subscription") {
sub = subs.find((s) => {
return s.subject === ctx.subject;
});
}
if (ctx.operation === "publish") {
// we have a no mux subscription
sub = subs.find((s) => {
return s.requestSubject === ctx.subject;
});
}
if (sub) {
sub.callback(err, {});
sub.close();
this.subs.delete(sub.sid);
return sub !== this.mux;
}
}
return false;
}
close() {
this.subs.forEach((sub) => {
sub.close();
});
}
}
exports.Subscriptions = Subscriptions;
class ProtocolHandler {
constructor(options, publisher) {
this._closed = false;
this.connected = false;
this.connectedOnce = false;
this.infoReceived = false;
this.noMorePublishing = false;
this.abortReconnect = false;
this.listeners = [];
this.pendingLimit = FLUSH_THRESHOLD;
this.outMsgs = 0;
this.inMsgs = 0;
this.outBytes = 0;
this.inBytes = 0;
this.options = options;
this.publisher = publisher;
this.subscriptions = new Subscriptions();
this.muxSubscriptions = new muxsubscription_1.MuxSubscription();
this.outbound = new databuffer_1.DataBuffer();
this.pongs = [];
this.whyClosed = "";
//@ts-ignore: options.pendingLimit is hidden
this.pendingLimit = options.pendingLimit || this.pendingLimit;
this.features = new semver_1.Features({ major: 0, minor: 0, micro: 0 });
this.connectPromise = null;
const servers = typeof options.servers === "string"
? [options.servers]
: options.servers;
this.servers = new servers_1.Servers(servers, {
randomize: !options.noRandomize,
});
this.closed = (0, util_1.deferred)();
this.parser = new parser_1.Parser(this);
this.heartbeats = new heartbeats_1.Heartbeat(this, this.options.pingInterval || options_1.DEFAULT_PING_INTERVAL, this.options.maxPingOut || options_1.DEFAULT_MAX_PING_OUT);
}
resetOutbound() {
this.outbound.reset();
const pongs = this.pongs;
this.pongs = [];
// reject the pongs - the disconnect from here shouldn't have a trace
// because that confuses API consumers
const err = core_1.NatsError.errorForCode(core_1.ErrorCode.Disconnect);
err.stack = "";
pongs.forEach((p) => {
p.reject(err);
});
this.parser = new parser_1.Parser(this);
this.infoReceived = false;
}
dispatchStatus(status) {
this.listeners.forEach((q) => {
q.push(status);
});
}
status() {
const iter = new queued_iterator_1.QueuedIteratorImpl();
this.listeners.push(iter);
return iter;
}
prepare() {
if (this.transport) {
this.transport.discard();
}
this.info = undefined;
this.resetOutbound();
const pong = (0, util_1.deferred)();
pong.catch(() => {
// provide at least one catch - as pong rejection can happen before it is expected
});
this.pongs.unshift(pong);
this.connectError = (err) => {
pong.reject(err);
};
this.transport = (0, transport_1.newTransport)();
this.transport.closed()
.then((_err) => __awaiter(this, void 0, void 0, function* () {
this.connected = false;
if (!this.isClosed()) {
// if the transport gave an error use that, otherwise
// we may have received a protocol error
yield this.disconnected(this.transport.closeError || this.lastError);
return;
}
}));
return pong;
}
disconnect() {
this.dispatchStatus({ type: core_1.DebugEvents.StaleConnection, data: "" });
this.transport.disconnect();
}
reconnect() {
if (this.connected) {
this.dispatchStatus({
type: core_1.DebugEvents.ClientInitiatedReconnect,
data: "",
});
this.transport.disconnect();
}
return Promise.resolve();
}
disconnected(err) {
return __awaiter(this, void 0, void 0, function* () {
this.dispatchStatus({
type: core_1.Events.Disconnect,
data: this.servers.getCurrentServer().toString(),
});
if (this.options.reconnect) {
yield this.dialLoop()
.then(() => {
var _a;
this.dispatchStatus({
type: core_1.Events.Reconnect,
data: this.servers.getCurrentServer().toString(),
});
// if we are here we reconnected, but we have an authentication
// that expired, we need to clean it up, otherwise we'll queue up
// two of these, and the default for the client will be to
// close, rather than attempt again - possibly they have an
// authenticator that dynamically updates
if (((_a = this.lastError) === null || _a === void 0 ? void 0 : _a.code) === core_1.ErrorCode.AuthenticationExpired) {
this.lastError = undefined;
}
})
.catch((err) => {
this._close(err);
});
}
else {
yield this._close(err);
}
});
}
dial(srv) {
return __awaiter(this, void 0, void 0, function* () {
const pong = this.prepare();
let timer;
try {
timer = (0, util_1.timeout)(this.options.timeout || 20000);
const cp = this.transport.connect(srv, this.options);
yield Promise.race([cp, timer]);
(() => __awaiter(this, void 0, void 0, function* () {
var _a, e_1, _b, _c;
try {
try {
for (var _d = true, _e = __asyncValues(this.transport), _f; _f = yield _e.next(), _a = _f.done, !_a; _d = true) {
_c = _f.value;
_d = false;
const b = _c;
this.parser.parse(b);
}
}
catch (e_1_1) { e_1 = { error: e_1_1 }; }
finally {
try {
if (!_d && !_a && (_b = _e.return)) yield _b.call(_e);
}
finally { if (e_1) throw e_1.error; }
}
}
catch (err) {
console.log("reader closed", err);
}
}))().then();
}
catch (err) {
pong.reject(err);
}
try {
yield Promise.race([timer, pong]);
if (timer) {
timer.cancel();
}
this.connected = true;
this.connectError = undefined;
this.sendSubscriptions();
this.connectedOnce = true;
this.server.didConnect = true;
this.server.reconnects = 0;
this.flushPending();
this.heartbeats.start();
}
catch (err) {
if (timer) {
timer.cancel();
}
yield this.transport.close(err);
throw err;
}
});
}
_doDial(srv) {
return __awaiter(this, void 0, void 0, function* () {
const alts = yield srv.resolve({
fn: (0, transport_1.getResolveFn)(),
debug: this.options.debug,
randomize: !this.options.noRandomize,
});
let lastErr = null;
for (const a of alts) {
try {
lastErr = null;
this.dispatchStatus({ type: core_1.DebugEvents.Reconnecting, data: a.toString() });
yield this.dial(a);
// if here we connected
return;
}
catch (err) {
lastErr = err;
}
}
// if we are here, we failed, and we have no additional
// alternatives for this server
throw lastErr;
});
}
dialLoop() {
if (this.connectPromise === null) {
this.connectPromise = this.dodialLoop();
this.connectPromise
.then(() => { })
.catch(() => { })
.finally(() => {
this.connectPromise = null;
});
}
return this.connectPromise;
}
dodialLoop() {
return __awaiter(this, void 0, void 0, function* () {
let lastError;
while (true) {
if (this._closed) {
// if we are disconnected, and close is called, the client
// still tries to reconnect - to match the reconnect policy
// in the case of close, want to stop.
this.servers.clear();
}
const wait = this.options.reconnectDelayHandler
? this.options.reconnectDelayHandler()
: options_1.DEFAULT_RECONNECT_TIME_WAIT;
let maxWait = wait;
const srv = this.selectServer();
if (!srv || this.abortReconnect) {
if (lastError) {
throw lastError;
}
else if (this.lastError) {
throw this.lastError;
}
else {
throw core_1.NatsError.errorForCode(core_1.ErrorCode.ConnectionRefused);
}
}
const now = Date.now();
if (srv.lastConnect === 0 || srv.lastConnect + wait <= now) {
srv.lastConnect = Date.now();
try {
yield this._doDial(srv);
break;
}
catch (err) {
lastError = err;
if (!this.connectedOnce) {
if (this.options.waitOnFirstConnect) {
continue;
}
this.servers.removeCurrentServer();
}
srv.reconnects++;
const mra = this.options.maxReconnectAttempts || 0;
if (mra !== -1 && srv.reconnects >= mra) {
this.servers.removeCurrentServer();
}
}
}
else {
maxWait = Math.min(maxWait, srv.lastConnect + wait - now);
yield (0, util_1.delay)(maxWait);
}
}
});
}
static connect(options, publisher) {
return __awaiter(this, void 0, void 0, function* () {
const h = new ProtocolHandler(options, publisher);
yield h.dialLoop();
return h;
});
}
static toError(s) {
const t = s ? s.toLowerCase() : "";
if (t.indexOf("permissions violation") !== -1) {
const err = new core_1.NatsError(s, core_1.ErrorCode.PermissionsViolation);
const m = s.match(/(Publish|Subscription) to "(\S+)"/);
if (m) {
err.permissionContext = {
operation: m[1].toLowerCase(),
subject: m[2],
};
}
return err;
}
else if (t.indexOf("authorization violation") !== -1) {
return new core_1.NatsError(s, core_1.ErrorCode.AuthorizationViolation);
}
else if (t.indexOf("user authentication expired") !== -1) {
return new core_1.NatsError(s, core_1.ErrorCode.AuthenticationExpired);
}
else if (t.indexOf("authentication timeout") !== -1) {
return new core_1.NatsError(s, core_1.ErrorCode.AuthenticationTimeout);
}
else {
return new core_1.NatsError(s, core_1.ErrorCode.ProtocolError);
}
}
processMsg(msg, data) {
this.inMsgs++;
this.inBytes += data.length;
if (!this.subscriptions.sidCounter) {
return;
}
const sub = this.subscriptions.get(msg.sid);
if (!sub) {
return;
}
sub.received += 1;
if (sub.callback) {
sub.callback(null, new msg_1.MsgImpl(msg, data, this));
}
if (sub.max !== undefined && sub.received >= sub.max) {
sub.unsubscribe();
}
}
processError(m) {
const s = (0, encoders_1.decode)(m);
const err = ProtocolHandler.toError(s);
const status = { type: core_1.Events.Error, data: err.code };
if (err.isPermissionError()) {
let isMuxPermissionError = false;
if (err.permissionContext) {
status.permissionContext = err.permissionContext;
const mux = this.subscriptions.getMux();
isMuxPermissionError = (mux === null || mux === void 0 ? void 0 : mux.subject) === err.permissionContext.subject;
}
this.subscriptions.handleError(err);
this.muxSubscriptions.handleError(isMuxPermissionError, err);
if (isMuxPermissionError) {
// remove the permission - enable it to be recreated
this.subscriptions.setMux(null);
}
}
this.dispatchStatus(status);
this.handleError(err);
}
handleError(err) {
if (err.isAuthError()) {
this.handleAuthError(err);
}
else if (err.isProtocolError()) {
this.lastError = err;
}
else if (err.isAuthTimeout()) {
this.lastError = err;
}
// fallthrough here
if (!err.isPermissionError()) {
this.lastError = err;
}
}
handleAuthError(err) {
if ((this.lastError && err.code === this.lastError.code) &&
this.options.ignoreAuthErrorAbort === false) {
this.abortReconnect = true;
}
if (this.connectError) {
this.connectError(err);
}
else {
this.disconnect();
}
}
processPing() {
this.transport.send(PONG_CMD);
}
processPong() {
const cb = this.pongs.shift();
if (cb) {
cb.resolve();
}
}
processInfo(m) {
const info = JSON.parse((0, encoders_1.decode)(m));
this.info = info;
const updates = this.options && this.options.ignoreClusterUpdates
? undefined
: this.servers.update(info, this.transport.isEncrypted());
if (!this.infoReceived) {
this.features.update((0, semver_1.parseSemVer)(info.version));
this.infoReceived = true;
if (this.transport.isEncrypted()) {
this.servers.updateTLSName();
}
// send connect
const { version, lang } = this.transport;
try {
const c = new Connect({ version, lang }, this.options, info.nonce);
if (info.headers) {
c.headers = true;
c.no_responders = true;
}
const cs = JSON.stringify(c);
this.transport.send((0, encoders_1.encode)(`CONNECT ${cs}${transport_1.CR_LF}`));
this.transport.send(PING_CMD);
}
catch (err) {
// if we are dying here, this is likely some an authenticator blowing up
this._close(err);
}
}
if (updates) {
this.dispatchStatus({ type: core_1.Events.Update, data: updates });
}
const ldm = info.ldm !== undefined ? info.ldm : false;
if (ldm) {
this.dispatchStatus({
type: core_1.Events.LDM,
data: this.servers.getCurrentServer().toString(),
});
}
}
push(e) {
switch (e.kind) {
case parser_1.Kind.MSG: {
const { msg, data } = e;
this.processMsg(msg, data);
break;
}
case parser_1.Kind.OK:
break;
case parser_1.Kind.ERR:
this.processError(e.data);
break;
case parser_1.Kind.PING:
this.processPing();
break;
case parser_1.Kind.PONG:
this.processPong();
break;
case parser_1.Kind.INFO:
this.processInfo(e.data);
break;
}
}
sendCommand(cmd, ...payloads) {
const len = this.outbound.length();
let buf;
if (typeof cmd === "string") {
buf = (0, encoders_1.encode)(cmd);
}
else {
buf = cmd;
}
this.outbound.fill(buf, ...payloads);
if (len === 0) {
queueMicrotask(() => {
this.flushPending();
});
}
else if (this.outbound.size() >= this.pendingLimit) {
// flush inline
this.flushPending();
}
}
publish(subject, payload = encoders_1.Empty, options) {
let data;
if (payload instanceof Uint8Array) {
data = payload;
}
else if (typeof payload === "string") {
data = encoders_1.TE.encode(payload);
}
else {
throw core_1.NatsError.errorForCode(core_1.ErrorCode.BadPayload);
}
let len = data.length;
options = options || {};
options.reply = options.reply || "";
let headers = encoders_1.Empty;
let hlen = 0;
if (options.headers) {
if (this.info && !this.info.headers) {
throw new core_1.NatsError("headers", core_1.ErrorCode.ServerOptionNotAvailable);
}
const hdrs = options.headers;
headers = hdrs.encode();
hlen = headers.length;
len = data.length + hlen;
}
if (this.info && len > this.info.max_payload) {
throw core_1.NatsError.errorForCode(core_1.ErrorCode.MaxPayloadExceeded);
}
this.outBytes += len;
this.outMsgs++;
let proto;
if (options.headers) {
if (options.reply) {
proto = `HPUB ${subject} ${options.reply} ${hlen} ${len}\r\n`;
}
else {
proto = `HPUB ${subject} ${hlen} ${len}\r\n`;
}
this.sendCommand(proto, headers, data, transport_1.CRLF);
}
else {
if (options.reply) {
proto = `PUB ${subject} ${options.reply} ${len}\r\n`;
}
else {
proto = `PUB ${subject} ${len}\r\n`;
}
this.sendCommand(proto, data, transport_1.CRLF);
}
}
request(r) {
this.initMux();
this.muxSubscriptions.add(r);
return r;
}
subscribe(s) {
this.subscriptions.add(s);
this._subunsub(s);
return s;
}
_sub(s) {
if (s.queue) {
this.sendCommand(`SUB ${s.subject} ${s.queue} ${s.sid}\r\n`);
}
else {
this.sendCommand(`SUB ${s.subject} ${s.sid}\r\n`);
}
}
_subunsub(s) {
this._sub(s);
if (s.max) {
this.unsubscribe(s, s.max);
}
return s;
}
unsubscribe(s, max) {
this.unsub(s, max);
if (s.max === undefined || s.received >= s.max) {
this.subscriptions.cancel(s);
}
}
unsub(s, max) {
if (!s || this.isClosed()) {
return;
}
if (max) {
this.sendCommand(`UNSUB ${s.sid} ${max}\r\n`);
}
else {
this.sendCommand(`UNSUB ${s.sid}\r\n`);
}
s.max = max;
}
resub(s, subject) {
if (!s || this.isClosed()) {
return;
}
this.unsub(s);
s.subject = subject;
this.subscriptions.resub(s);
// we don't auto-unsub here because we don't
// really know "processed"
this._sub(s);
}
flush(p) {
if (!p) {
p = (0, util_1.deferred)();
}
this.pongs.push(p);
this.outbound.fill(PING_CMD);
this.flushPending();
return p;
}
sendSubscriptions() {
const cmds = [];
this.subscriptions.all().forEach((s) => {
const sub = s;
if (sub.queue) {
cmds.push(`SUB ${sub.subject} ${sub.queue} ${sub.sid}${transport_1.CR_LF}`);
}
else {
cmds.push(`SUB ${sub.subject} ${sub.sid}${transport_1.CR_LF}`);
}
});
if (cmds.length) {
this.transport.send((0, encoders_1.encode)(cmds.join("")));
}
}
_close(err) {
return __awaiter(this, void 0, void 0, function* () {
if (this._closed) {
return;
}
this.whyClosed = new Error("close trace").stack || "";
this.heartbeats.cancel();
if (this.connectError) {
this.connectError(err);
this.connectError = undefined;
}
this.muxSubscriptions.close();
this.subscriptions.close();
this.listeners.forEach((l) => {
l.stop();
});
this._closed = true;
yield this.transport.close(err);
yield this.closed.resolve(err);
});
}
close() {
return this._close();
}
isClosed() {
return this._closed;
}
drain() {
const subs = this.subscriptions.all();
const promises = [];
subs.forEach((sub) => {
promises.push(sub.drain());
});
return Promise.all(promises)
.then(() => __awaiter(this, void 0, void 0, function* () {
this.noMorePublishing = true;
yield this.flush();
return this.close();
}))
.catch(() => {
// cannot happen
});
}
flushPending() {
if (!this.infoReceived || !this.connected) {
return;
}
if (this.outbound.size()) {
const d = this.outbound.drain();
this.transport.send(d);
}
}
initMux() {
const mux = this.subscriptions.getMux();
if (!mux) {
const inbox = this.muxSubscriptions.init(this.options.inboxPrefix);
// dot is already part of mux
const sub = new SubscriptionImpl(this, `${inbox}*`);
sub.callback = this.muxSubscriptions.dispatcher();
this.subscriptions.setMux(sub);
this.subscribe(sub);
}
}
selectServer() {
const server = this.servers.selectServer();
if (server === undefined) {
return undefined;
}
// Place in client context.
this.server = server;
return this.server;
}
getServer() {
return this.server;
}
}
exports.ProtocolHandler = ProtocolHandler;
//# sourceMappingURL=protocol.js.map