nats
Version:
Node.js client for NATS, a lightweight, high-performance cloud native messaging system
704 lines • 26.5 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.State = exports.Parser = exports.describe = exports.Kind = void 0;
// deno-lint-ignore-file no-undef
/*
* Copyright 2020-2021 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 denobuffer_1 = require("./denobuffer");
const encoders_1 = require("./encoders");
var Kind;
(function (Kind) {
Kind[Kind["OK"] = 0] = "OK";
Kind[Kind["ERR"] = 1] = "ERR";
Kind[Kind["MSG"] = 2] = "MSG";
Kind[Kind["INFO"] = 3] = "INFO";
Kind[Kind["PING"] = 4] = "PING";
Kind[Kind["PONG"] = 5] = "PONG";
})(Kind || (exports.Kind = Kind = {}));
function describe(e) {
let ks;
let data = "";
switch (e.kind) {
case Kind.MSG:
ks = "MSG";
break;
case Kind.OK:
ks = "OK";
break;
case Kind.ERR:
ks = "ERR";
data = encoders_1.TD.decode(e.data);
break;
case Kind.PING:
ks = "PING";
break;
case Kind.PONG:
ks = "PONG";
break;
case Kind.INFO:
ks = "INFO";
data = encoders_1.TD.decode(e.data);
}
return `${ks}: ${data}`;
}
exports.describe = describe;
function newMsgArg() {
const ma = {};
ma.sid = -1;
ma.hdr = -1;
ma.size = -1;
return ma;
}
const ASCII_0 = 48;
const ASCII_9 = 57;
// This is an almost verbatim port of the Go NATS parser
// https://github.com/nats-io/nats.go/blob/master/parser.go
class Parser {
constructor(dispatcher) {
this.dispatcher = dispatcher;
this.state = State.OP_START;
this.as = 0;
this.drop = 0;
this.hdr = 0;
}
parse(buf) {
let i;
for (i = 0; i < buf.length; i++) {
const b = buf[i];
switch (this.state) {
case State.OP_START:
switch (b) {
case cc.M:
case cc.m:
this.state = State.OP_M;
this.hdr = -1;
this.ma = newMsgArg();
break;
case cc.H:
case cc.h:
this.state = State.OP_H;
this.hdr = 0;
this.ma = newMsgArg();
break;
case cc.P:
case cc.p:
this.state = State.OP_P;
break;
case cc.PLUS:
this.state = State.OP_PLUS;
break;
case cc.MINUS:
this.state = State.OP_MINUS;
break;
case cc.I:
case cc.i:
this.state = State.OP_I;
break;
default:
throw this.fail(buf.subarray(i));
}
break;
case State.OP_H:
switch (b) {
case cc.M:
case cc.m:
this.state = State.OP_M;
break;
default:
throw this.fail(buf.subarray(i));
}
break;
case State.OP_M:
switch (b) {
case cc.S:
case cc.s:
this.state = State.OP_MS;
break;
default:
throw this.fail(buf.subarray(i));
}
break;
case State.OP_MS:
switch (b) {
case cc.G:
case cc.g:
this.state = State.OP_MSG;
break;
default:
throw this.fail(buf.subarray(i));
}
break;
case State.OP_MSG:
switch (b) {
case cc.SPACE:
case cc.TAB:
this.state = State.OP_MSG_SPC;
break;
default:
throw this.fail(buf.subarray(i));
}
break;
case State.OP_MSG_SPC:
switch (b) {
case cc.SPACE:
case cc.TAB:
continue;
default:
this.state = State.MSG_ARG;
this.as = i;
}
break;
case State.MSG_ARG:
switch (b) {
case cc.CR:
this.drop = 1;
break;
case cc.NL: {
const arg = this.argBuf
? this.argBuf.bytes()
: buf.subarray(this.as, i - this.drop);
this.processMsgArgs(arg);
this.drop = 0;
this.as = i + 1;
this.state = State.MSG_PAYLOAD;
// jump ahead with the index. If this overruns
// what is left we fall out and process a split buffer.
i = this.as + this.ma.size - 1;
break;
}
default:
if (this.argBuf) {
this.argBuf.writeByte(b);
}
}
break;
case State.MSG_PAYLOAD:
if (this.msgBuf) {
if (this.msgBuf.length >= this.ma.size) {
const data = this.msgBuf.bytes({ copy: false });
this.dispatcher.push({ kind: Kind.MSG, msg: this.ma, data: data });
this.argBuf = undefined;
this.msgBuf = undefined;
this.state = State.MSG_END;
}
else {
let toCopy = this.ma.size - this.msgBuf.length;
const avail = buf.length - i;
if (avail < toCopy) {
toCopy = avail;
}
if (toCopy > 0) {
this.msgBuf.write(buf.subarray(i, i + toCopy));
i = (i + toCopy) - 1;
}
else {
this.msgBuf.writeByte(b);
}
}
}
else if (i - this.as >= this.ma.size) {
this.dispatcher.push({ kind: Kind.MSG, msg: this.ma, data: buf.subarray(this.as, i) });
this.argBuf = undefined;
this.msgBuf = undefined;
this.state = State.MSG_END;
}
break;
case State.MSG_END:
switch (b) {
case cc.NL:
this.drop = 0;
this.as = i + 1;
this.state = State.OP_START;
break;
default:
continue;
}
break;
case State.OP_PLUS:
switch (b) {
case cc.O:
case cc.o:
this.state = State.OP_PLUS_O;
break;
default:
throw this.fail(buf.subarray(i));
}
break;
case State.OP_PLUS_O:
switch (b) {
case cc.K:
case cc.k:
this.state = State.OP_PLUS_OK;
break;
default:
throw this.fail(buf.subarray(i));
}
break;
case State.OP_PLUS_OK:
switch (b) {
case cc.NL:
this.dispatcher.push({ kind: Kind.OK });
this.drop = 0;
this.state = State.OP_START;
break;
}
break;
case State.OP_MINUS:
switch (b) {
case cc.E:
case cc.e:
this.state = State.OP_MINUS_E;
break;
default:
throw this.fail(buf.subarray(i));
}
break;
case State.OP_MINUS_E:
switch (b) {
case cc.R:
case cc.r:
this.state = State.OP_MINUS_ER;
break;
default:
throw this.fail(buf.subarray(i));
}
break;
case State.OP_MINUS_ER:
switch (b) {
case cc.R:
case cc.r:
this.state = State.OP_MINUS_ERR;
break;
default:
throw this.fail(buf.subarray(i));
}
break;
case State.OP_MINUS_ERR:
switch (b) {
case cc.SPACE:
case cc.TAB:
this.state = State.OP_MINUS_ERR_SPC;
break;
default:
throw this.fail(buf.subarray(i));
}
break;
case State.OP_MINUS_ERR_SPC:
switch (b) {
case cc.SPACE:
case cc.TAB:
continue;
default:
this.state = State.MINUS_ERR_ARG;
this.as = i;
}
break;
case State.MINUS_ERR_ARG:
switch (b) {
case cc.CR:
this.drop = 1;
break;
case cc.NL: {
let arg;
if (this.argBuf) {
arg = this.argBuf.bytes();
this.argBuf = undefined;
}
else {
arg = buf.subarray(this.as, i - this.drop);
}
this.dispatcher.push({ kind: Kind.ERR, data: arg });
this.drop = 0;
this.as = i + 1;
this.state = State.OP_START;
break;
}
default:
if (this.argBuf) {
this.argBuf.write(Uint8Array.of(b));
}
}
break;
case State.OP_P:
switch (b) {
case cc.I:
case cc.i:
this.state = State.OP_PI;
break;
case cc.O:
case cc.o:
this.state = State.OP_PO;
break;
default:
throw this.fail(buf.subarray(i));
}
break;
case State.OP_PO:
switch (b) {
case cc.N:
case cc.n:
this.state = State.OP_PON;
break;
default:
throw this.fail(buf.subarray(i));
}
break;
case State.OP_PON:
switch (b) {
case cc.G:
case cc.g:
this.state = State.OP_PONG;
break;
default:
throw this.fail(buf.subarray(i));
}
break;
case State.OP_PONG:
switch (b) {
case cc.NL:
this.dispatcher.push({ kind: Kind.PONG });
this.drop = 0;
this.state = State.OP_START;
break;
}
break;
case State.OP_PI:
switch (b) {
case cc.N:
case cc.n:
this.state = State.OP_PIN;
break;
default:
throw this.fail(buf.subarray(i));
}
break;
case State.OP_PIN:
switch (b) {
case cc.G:
case cc.g:
this.state = State.OP_PING;
break;
default:
throw this.fail(buf.subarray(i));
}
break;
case State.OP_PING:
switch (b) {
case cc.NL:
this.dispatcher.push({ kind: Kind.PING });
this.drop = 0;
this.state = State.OP_START;
break;
}
break;
case State.OP_I:
switch (b) {
case cc.N:
case cc.n:
this.state = State.OP_IN;
break;
default:
throw this.fail(buf.subarray(i));
}
break;
case State.OP_IN:
switch (b) {
case cc.F:
case cc.f:
this.state = State.OP_INF;
break;
default:
throw this.fail(buf.subarray(i));
}
break;
case State.OP_INF:
switch (b) {
case cc.O:
case cc.o:
this.state = State.OP_INFO;
break;
default:
throw this.fail(buf.subarray(i));
}
break;
case State.OP_INFO:
switch (b) {
case cc.SPACE:
case cc.TAB:
this.state = State.OP_INFO_SPC;
break;
default:
throw this.fail(buf.subarray(i));
}
break;
case State.OP_INFO_SPC:
switch (b) {
case cc.SPACE:
case cc.TAB:
continue;
default:
this.state = State.INFO_ARG;
this.as = i;
}
break;
case State.INFO_ARG:
switch (b) {
case cc.CR:
this.drop = 1;
break;
case cc.NL: {
let arg;
if (this.argBuf) {
arg = this.argBuf.bytes();
this.argBuf = undefined;
}
else {
arg = buf.subarray(this.as, i - this.drop);
}
this.dispatcher.push({ kind: Kind.INFO, data: arg });
this.drop = 0;
this.as = i + 1;
this.state = State.OP_START;
break;
}
default:
if (this.argBuf) {
this.argBuf.writeByte(b);
}
}
break;
default:
throw this.fail(buf.subarray(i));
}
}
if ((this.state === State.MSG_ARG || this.state === State.MINUS_ERR_ARG ||
this.state === State.INFO_ARG) && !this.argBuf) {
this.argBuf = new denobuffer_1.DenoBuffer(buf.subarray(this.as, i - this.drop));
}
if (this.state === State.MSG_PAYLOAD && !this.msgBuf) {
if (!this.argBuf) {
this.cloneMsgArg();
}
this.msgBuf = new denobuffer_1.DenoBuffer(buf.subarray(this.as));
}
}
cloneMsgArg() {
const s = this.ma.subject.length;
const r = this.ma.reply ? this.ma.reply.length : 0;
const buf = new Uint8Array(s + r);
buf.set(this.ma.subject);
if (this.ma.reply) {
buf.set(this.ma.reply, s);
}
this.argBuf = new denobuffer_1.DenoBuffer(buf);
this.ma.subject = buf.subarray(0, s);
if (this.ma.reply) {
this.ma.reply = buf.subarray(s);
}
}
processMsgArgs(arg) {
if (this.hdr >= 0) {
return this.processHeaderMsgArgs(arg);
}
const args = [];
let start = -1;
for (let i = 0; i < arg.length; i++) {
const b = arg[i];
switch (b) {
case cc.SPACE:
case cc.TAB:
case cc.CR:
case cc.NL:
if (start >= 0) {
args.push(arg.subarray(start, i));
start = -1;
}
break;
default:
if (start < 0) {
start = i;
}
}
}
if (start >= 0) {
args.push(arg.subarray(start));
}
switch (args.length) {
case 3:
this.ma.subject = args[0];
this.ma.sid = this.protoParseInt(args[1]);
this.ma.reply = undefined;
this.ma.size = this.protoParseInt(args[2]);
break;
case 4:
this.ma.subject = args[0];
this.ma.sid = this.protoParseInt(args[1]);
this.ma.reply = args[2];
this.ma.size = this.protoParseInt(args[3]);
break;
default:
throw this.fail(arg, "processMsgArgs Parse Error");
}
if (this.ma.sid < 0) {
throw this.fail(arg, "processMsgArgs Bad or Missing Sid Error");
}
if (this.ma.size < 0) {
throw this.fail(arg, "processMsgArgs Bad or Missing Size Error");
}
}
fail(data, label = "") {
if (!label) {
label = `parse error [${this.state}]`;
}
else {
label = `${label} [${this.state}]`;
}
return new Error(`${label}: ${encoders_1.TD.decode(data)}`);
}
processHeaderMsgArgs(arg) {
const args = [];
let start = -1;
for (let i = 0; i < arg.length; i++) {
const b = arg[i];
switch (b) {
case cc.SPACE:
case cc.TAB:
case cc.CR:
case cc.NL:
if (start >= 0) {
args.push(arg.subarray(start, i));
start = -1;
}
break;
default:
if (start < 0) {
start = i;
}
}
}
if (start >= 0) {
args.push(arg.subarray(start));
}
switch (args.length) {
case 4:
this.ma.subject = args[0];
this.ma.sid = this.protoParseInt(args[1]);
this.ma.reply = undefined;
this.ma.hdr = this.protoParseInt(args[2]);
this.ma.size = this.protoParseInt(args[3]);
break;
case 5:
this.ma.subject = args[0];
this.ma.sid = this.protoParseInt(args[1]);
this.ma.reply = args[2];
this.ma.hdr = this.protoParseInt(args[3]);
this.ma.size = this.protoParseInt(args[4]);
break;
default:
throw this.fail(arg, "processHeaderMsgArgs Parse Error");
}
if (this.ma.sid < 0) {
throw this.fail(arg, "processHeaderMsgArgs Bad or Missing Sid Error");
}
if (this.ma.hdr < 0 || this.ma.hdr > this.ma.size) {
throw this.fail(arg, "processHeaderMsgArgs Bad or Missing Header Size Error");
}
if (this.ma.size < 0) {
throw this.fail(arg, "processHeaderMsgArgs Bad or Missing Size Error");
}
}
protoParseInt(a) {
if (a.length === 0) {
return -1;
}
let n = 0;
for (let i = 0; i < a.length; i++) {
if (a[i] < ASCII_0 || a[i] > ASCII_9) {
return -1;
}
n = n * 10 + (a[i] - ASCII_0);
}
return n;
}
}
exports.Parser = Parser;
var State;
(function (State) {
State[State["OP_START"] = 0] = "OP_START";
State[State["OP_PLUS"] = 1] = "OP_PLUS";
State[State["OP_PLUS_O"] = 2] = "OP_PLUS_O";
State[State["OP_PLUS_OK"] = 3] = "OP_PLUS_OK";
State[State["OP_MINUS"] = 4] = "OP_MINUS";
State[State["OP_MINUS_E"] = 5] = "OP_MINUS_E";
State[State["OP_MINUS_ER"] = 6] = "OP_MINUS_ER";
State[State["OP_MINUS_ERR"] = 7] = "OP_MINUS_ERR";
State[State["OP_MINUS_ERR_SPC"] = 8] = "OP_MINUS_ERR_SPC";
State[State["MINUS_ERR_ARG"] = 9] = "MINUS_ERR_ARG";
State[State["OP_M"] = 10] = "OP_M";
State[State["OP_MS"] = 11] = "OP_MS";
State[State["OP_MSG"] = 12] = "OP_MSG";
State[State["OP_MSG_SPC"] = 13] = "OP_MSG_SPC";
State[State["MSG_ARG"] = 14] = "MSG_ARG";
State[State["MSG_PAYLOAD"] = 15] = "MSG_PAYLOAD";
State[State["MSG_END"] = 16] = "MSG_END";
State[State["OP_H"] = 17] = "OP_H";
State[State["OP_P"] = 18] = "OP_P";
State[State["OP_PI"] = 19] = "OP_PI";
State[State["OP_PIN"] = 20] = "OP_PIN";
State[State["OP_PING"] = 21] = "OP_PING";
State[State["OP_PO"] = 22] = "OP_PO";
State[State["OP_PON"] = 23] = "OP_PON";
State[State["OP_PONG"] = 24] = "OP_PONG";
State[State["OP_I"] = 25] = "OP_I";
State[State["OP_IN"] = 26] = "OP_IN";
State[State["OP_INF"] = 27] = "OP_INF";
State[State["OP_INFO"] = 28] = "OP_INFO";
State[State["OP_INFO_SPC"] = 29] = "OP_INFO_SPC";
State[State["INFO_ARG"] = 30] = "INFO_ARG";
})(State || (exports.State = State = {}));
var cc;
(function (cc) {
cc[cc["CR"] = "\r".charCodeAt(0)] = "CR";
cc[cc["E"] = "E".charCodeAt(0)] = "E";
cc[cc["e"] = "e".charCodeAt(0)] = "e";
cc[cc["F"] = "F".charCodeAt(0)] = "F";
cc[cc["f"] = "f".charCodeAt(0)] = "f";
cc[cc["G"] = "G".charCodeAt(0)] = "G";
cc[cc["g"] = "g".charCodeAt(0)] = "g";
cc[cc["H"] = "H".charCodeAt(0)] = "H";
cc[cc["h"] = "h".charCodeAt(0)] = "h";
cc[cc["I"] = "I".charCodeAt(0)] = "I";
cc[cc["i"] = "i".charCodeAt(0)] = "i";
cc[cc["K"] = "K".charCodeAt(0)] = "K";
cc[cc["k"] = "k".charCodeAt(0)] = "k";
cc[cc["M"] = "M".charCodeAt(0)] = "M";
cc[cc["m"] = "m".charCodeAt(0)] = "m";
cc[cc["MINUS"] = "-".charCodeAt(0)] = "MINUS";
cc[cc["N"] = "N".charCodeAt(0)] = "N";
cc[cc["n"] = "n".charCodeAt(0)] = "n";
cc[cc["NL"] = "\n".charCodeAt(0)] = "NL";
cc[cc["O"] = "O".charCodeAt(0)] = "O";
cc[cc["o"] = "o".charCodeAt(0)] = "o";
cc[cc["P"] = "P".charCodeAt(0)] = "P";
cc[cc["p"] = "p".charCodeAt(0)] = "p";
cc[cc["PLUS"] = "+".charCodeAt(0)] = "PLUS";
cc[cc["R"] = "R".charCodeAt(0)] = "R";
cc[cc["r"] = "r".charCodeAt(0)] = "r";
cc[cc["S"] = "S".charCodeAt(0)] = "S";
cc[cc["s"] = "s".charCodeAt(0)] = "s";
cc[cc["SPACE"] = " ".charCodeAt(0)] = "SPACE";
cc[cc["TAB"] = "\t".charCodeAt(0)] = "TAB";
})(cc || (cc = {}));
//# sourceMappingURL=parser.js.map