add websocket server to this dir. fix stuff for client

This commit is contained in:
2024-10-30 15:12:52 -05:00
parent f8152c6db8
commit 4886bc5b1f
3058 changed files with 1201180 additions and 2 deletions

View File

@ -0,0 +1 @@
export {};

23
websocket_server/node_modules/pg-protocol/dist/b.js generated vendored Normal file
View File

@ -0,0 +1,23 @@
"use strict";
// file for microbenchmarking
Object.defineProperty(exports, "__esModule", { value: true });
const buffer_reader_1 = require("./buffer-reader");
const LOOPS = 1000;
let count = 0;
let start = Date.now();
const reader = new buffer_reader_1.BufferReader();
const buffer = Buffer.from([33, 33, 33, 33, 33, 33, 33, 0]);
const run = () => {
if (count > LOOPS) {
console.log(Date.now() - start);
return;
}
count++;
for (let i = 0; i < LOOPS; i++) {
reader.setBuffer(0, buffer);
reader.cstring();
}
setImmediate(run);
};
run();
//# sourceMappingURL=b.js.map

View File

@ -0,0 +1 @@
{"version":3,"file":"b.js","sourceRoot":"","sources":["../src/b.ts"],"names":[],"mappings":";AAAA,6BAA6B;;AAE7B,mDAA8C;AAE9C,MAAM,KAAK,GAAG,IAAI,CAAA;AAClB,IAAI,KAAK,GAAG,CAAC,CAAA;AACb,IAAI,KAAK,GAAG,IAAI,CAAC,GAAG,EAAE,CAAA;AAEtB,MAAM,MAAM,GAAG,IAAI,4BAAY,EAAE,CAAA;AACjC,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC,CAAA;AAE3D,MAAM,GAAG,GAAG,GAAG,EAAE;IACf,IAAI,KAAK,GAAG,KAAK,EAAE;QACjB,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC,CAAA;QAC/B,OAAM;KACP;IACD,KAAK,EAAE,CAAA;IACP,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,EAAE,CAAC,EAAE,EAAE;QAC9B,MAAM,CAAC,SAAS,CAAC,CAAC,EAAE,MAAM,CAAC,CAAA;QAC3B,MAAM,CAAC,OAAO,EAAE,CAAA;KACjB;IACD,YAAY,CAAC,GAAG,CAAC,CAAA;AACnB,CAAC,CAAA;AAED,GAAG,EAAE,CAAA"}

View File

@ -0,0 +1,14 @@
/// <reference types="node" />
export declare class BufferReader {
private offset;
private buffer;
private encoding;
constructor(offset?: number);
setBuffer(offset: number, buffer: Buffer): void;
int16(): number;
byte(): number;
int32(): number;
string(length: number): string;
cstring(): string;
bytes(length: number): Buffer;
}

View File

@ -0,0 +1,50 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.BufferReader = void 0;
const emptyBuffer = Buffer.allocUnsafe(0);
class BufferReader {
constructor(offset = 0) {
this.offset = offset;
this.buffer = emptyBuffer;
// TODO(bmc): support non-utf8 encoding?
this.encoding = 'utf-8';
}
setBuffer(offset, buffer) {
this.offset = offset;
this.buffer = buffer;
}
int16() {
const result = this.buffer.readInt16BE(this.offset);
this.offset += 2;
return result;
}
byte() {
const result = this.buffer[this.offset];
this.offset++;
return result;
}
int32() {
const result = this.buffer.readInt32BE(this.offset);
this.offset += 4;
return result;
}
string(length) {
const result = this.buffer.toString(this.encoding, this.offset, this.offset + length);
this.offset += length;
return result;
}
cstring() {
const start = this.offset;
let end = start;
while (this.buffer[end++] !== 0) { }
this.offset = end;
return this.buffer.toString(this.encoding, start, end - 1);
}
bytes(length) {
const result = this.buffer.slice(this.offset, this.offset + length);
this.offset += length;
return result;
}
}
exports.BufferReader = BufferReader;
//# sourceMappingURL=buffer-reader.js.map

View File

@ -0,0 +1 @@
{"version":3,"file":"buffer-reader.js","sourceRoot":"","sources":["../src/buffer-reader.ts"],"names":[],"mappings":";;;AAAA,MAAM,WAAW,GAAG,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC,CAAA;AAEzC,MAAa,YAAY;IAMvB,YAAoB,SAAiB,CAAC;QAAlB,WAAM,GAAN,MAAM,CAAY;QAL9B,WAAM,GAAW,WAAW,CAAA;QAEpC,wCAAwC;QAChC,aAAQ,GAAW,OAAO,CAAA;IAEO,CAAC;IAEnC,SAAS,CAAC,MAAc,EAAE,MAAc;QAC7C,IAAI,CAAC,MAAM,GAAG,MAAM,CAAA;QACpB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAA;IACtB,CAAC;IAEM,KAAK;QACV,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;QACnD,IAAI,CAAC,MAAM,IAAI,CAAC,CAAA;QAChB,OAAO,MAAM,CAAA;IACf,CAAC;IAEM,IAAI;QACT,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;QACvC,IAAI,CAAC,MAAM,EAAE,CAAA;QACb,OAAO,MAAM,CAAA;IACf,CAAC;IAEM,KAAK;QACV,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;QACnD,IAAI,CAAC,MAAM,IAAI,CAAC,CAAA;QAChB,OAAO,MAAM,CAAA;IACf,CAAC;IAEM,MAAM,CAAC,MAAc;QAC1B,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC,CAAA;QACrF,IAAI,CAAC,MAAM,IAAI,MAAM,CAAA;QACrB,OAAO,MAAM,CAAA;IACf,CAAC;IAEM,OAAO;QACZ,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAA;QACzB,IAAI,GAAG,GAAG,KAAK,CAAA;QACf,OAAO,IAAI,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,EAAE,GAAE;QACnC,IAAI,CAAC,MAAM,GAAG,GAAG,CAAA;QACjB,OAAO,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,QAAQ,EAAE,KAAK,EAAE,GAAG,GAAG,CAAC,CAAC,CAAA;IAC5D,CAAC;IAEM,KAAK,CAAC,MAAc;QACzB,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC,CAAA;QACnE,IAAI,CAAC,MAAM,IAAI,MAAM,CAAA;QACrB,OAAO,MAAM,CAAA;IACf,CAAC;CACF;AAlDD,oCAkDC"}

View File

@ -0,0 +1,16 @@
/// <reference types="node" />
export declare class Writer {
private size;
private buffer;
private offset;
private headerPosition;
constructor(size?: number);
private ensure;
addInt32(num: number): Writer;
addInt16(num: number): Writer;
addCString(string: string): Writer;
addString(string?: string): Writer;
add(otherBuffer: Buffer): Writer;
private join;
flush(code?: number): Buffer;
}

View File

@ -0,0 +1,81 @@
"use strict";
//binary data writer tuned for encoding binary specific to the postgres binary protocol
Object.defineProperty(exports, "__esModule", { value: true });
exports.Writer = void 0;
class Writer {
constructor(size = 256) {
this.size = size;
this.offset = 5;
this.headerPosition = 0;
this.buffer = Buffer.allocUnsafe(size);
}
ensure(size) {
var remaining = this.buffer.length - this.offset;
if (remaining < size) {
var oldBuffer = this.buffer;
// exponential growth factor of around ~ 1.5
// https://stackoverflow.com/questions/2269063/buffer-growth-strategy
var newSize = oldBuffer.length + (oldBuffer.length >> 1) + size;
this.buffer = Buffer.allocUnsafe(newSize);
oldBuffer.copy(this.buffer);
}
}
addInt32(num) {
this.ensure(4);
this.buffer[this.offset++] = (num >>> 24) & 0xff;
this.buffer[this.offset++] = (num >>> 16) & 0xff;
this.buffer[this.offset++] = (num >>> 8) & 0xff;
this.buffer[this.offset++] = (num >>> 0) & 0xff;
return this;
}
addInt16(num) {
this.ensure(2);
this.buffer[this.offset++] = (num >>> 8) & 0xff;
this.buffer[this.offset++] = (num >>> 0) & 0xff;
return this;
}
addCString(string) {
if (!string) {
this.ensure(1);
}
else {
var len = Buffer.byteLength(string);
this.ensure(len + 1); // +1 for null terminator
this.buffer.write(string, this.offset, 'utf-8');
this.offset += len;
}
this.buffer[this.offset++] = 0; // null terminator
return this;
}
addString(string = '') {
var len = Buffer.byteLength(string);
this.ensure(len);
this.buffer.write(string, this.offset);
this.offset += len;
return this;
}
add(otherBuffer) {
this.ensure(otherBuffer.length);
otherBuffer.copy(this.buffer, this.offset);
this.offset += otherBuffer.length;
return this;
}
join(code) {
if (code) {
this.buffer[this.headerPosition] = code;
//length is everything in this packet minus the code
const length = this.offset - (this.headerPosition + 1);
this.buffer.writeInt32BE(length, this.headerPosition + 1);
}
return this.buffer.slice(code ? 0 : 5, this.offset);
}
flush(code) {
var result = this.join(code);
this.offset = 5;
this.headerPosition = 0;
this.buffer = Buffer.allocUnsafe(this.size);
return result;
}
}
exports.Writer = Writer;
//# sourceMappingURL=buffer-writer.js.map

View File

@ -0,0 +1 @@
{"version":3,"file":"buffer-writer.js","sourceRoot":"","sources":["../src/buffer-writer.ts"],"names":[],"mappings":"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"}

View File

@ -0,0 +1 @@
export {};

View File

@ -0,0 +1,501 @@
"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 __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const test_buffers_1 = __importDefault(require("./testing/test-buffers"));
const buffer_list_1 = __importDefault(require("./testing/buffer-list"));
const _1 = require(".");
const assert_1 = __importDefault(require("assert"));
const stream_1 = require("stream");
var authOkBuffer = test_buffers_1.default.authenticationOk();
var paramStatusBuffer = test_buffers_1.default.parameterStatus('client_encoding', 'UTF8');
var readyForQueryBuffer = test_buffers_1.default.readyForQuery();
var backendKeyDataBuffer = test_buffers_1.default.backendKeyData(1, 2);
var commandCompleteBuffer = test_buffers_1.default.commandComplete('SELECT 3');
var parseCompleteBuffer = test_buffers_1.default.parseComplete();
var bindCompleteBuffer = test_buffers_1.default.bindComplete();
var portalSuspendedBuffer = test_buffers_1.default.portalSuspended();
var row1 = {
name: 'id',
tableID: 1,
attributeNumber: 2,
dataTypeID: 3,
dataTypeSize: 4,
typeModifier: 5,
formatCode: 0,
};
var oneRowDescBuff = test_buffers_1.default.rowDescription([row1]);
row1.name = 'bang';
var twoRowBuf = test_buffers_1.default.rowDescription([
row1,
{
name: 'whoah',
tableID: 10,
attributeNumber: 11,
dataTypeID: 12,
dataTypeSize: 13,
typeModifier: 14,
formatCode: 0,
},
]);
var emptyRowFieldBuf = new buffer_list_1.default().addInt16(0).join(true, 'D');
var emptyRowFieldBuf = test_buffers_1.default.dataRow([]);
var oneFieldBuf = new buffer_list_1.default()
.addInt16(1) // number of fields
.addInt32(5) // length of bytes of fields
.addCString('test')
.join(true, 'D');
var oneFieldBuf = test_buffers_1.default.dataRow(['test']);
var expectedAuthenticationOkayMessage = {
name: 'authenticationOk',
length: 8,
};
var expectedParameterStatusMessage = {
name: 'parameterStatus',
parameterName: 'client_encoding',
parameterValue: 'UTF8',
length: 25,
};
var expectedBackendKeyDataMessage = {
name: 'backendKeyData',
processID: 1,
secretKey: 2,
};
var expectedReadyForQueryMessage = {
name: 'readyForQuery',
length: 5,
status: 'I',
};
var expectedCommandCompleteMessage = {
name: 'commandComplete',
length: 13,
text: 'SELECT 3',
};
var emptyRowDescriptionBuffer = new buffer_list_1.default()
.addInt16(0) // number of fields
.join(true, 'T');
var expectedEmptyRowDescriptionMessage = {
name: 'rowDescription',
length: 6,
fieldCount: 0,
fields: [],
};
var expectedOneRowMessage = {
name: 'rowDescription',
length: 27,
fieldCount: 1,
fields: [
{
name: 'id',
tableID: 1,
columnID: 2,
dataTypeID: 3,
dataTypeSize: 4,
dataTypeModifier: 5,
format: 'text',
},
],
};
var expectedTwoRowMessage = {
name: 'rowDescription',
length: 53,
fieldCount: 2,
fields: [
{
name: 'bang',
tableID: 1,
columnID: 2,
dataTypeID: 3,
dataTypeSize: 4,
dataTypeModifier: 5,
format: 'text',
},
{
name: 'whoah',
tableID: 10,
columnID: 11,
dataTypeID: 12,
dataTypeSize: 13,
dataTypeModifier: 14,
format: 'text',
},
],
};
var emptyParameterDescriptionBuffer = new buffer_list_1.default()
.addInt16(0) // number of parameters
.join(true, 't');
var oneParameterDescBuf = test_buffers_1.default.parameterDescription([1111]);
var twoParameterDescBuf = test_buffers_1.default.parameterDescription([2222, 3333]);
var expectedEmptyParameterDescriptionMessage = {
name: 'parameterDescription',
length: 6,
parameterCount: 0,
dataTypeIDs: [],
};
var expectedOneParameterMessage = {
name: 'parameterDescription',
length: 10,
parameterCount: 1,
dataTypeIDs: [1111],
};
var expectedTwoParameterMessage = {
name: 'parameterDescription',
length: 14,
parameterCount: 2,
dataTypeIDs: [2222, 3333],
};
var testForMessage = function (buffer, expectedMessage) {
it('recieves and parses ' + expectedMessage.name, () => __awaiter(this, void 0, void 0, function* () {
const messages = yield parseBuffers([buffer]);
const [lastMessage] = messages;
for (const key in expectedMessage) {
assert_1.default.deepEqual(lastMessage[key], expectedMessage[key]);
}
}));
};
var plainPasswordBuffer = test_buffers_1.default.authenticationCleartextPassword();
var md5PasswordBuffer = test_buffers_1.default.authenticationMD5Password();
var SASLBuffer = test_buffers_1.default.authenticationSASL();
var SASLContinueBuffer = test_buffers_1.default.authenticationSASLContinue();
var SASLFinalBuffer = test_buffers_1.default.authenticationSASLFinal();
var expectedPlainPasswordMessage = {
name: 'authenticationCleartextPassword',
};
var expectedMD5PasswordMessage = {
name: 'authenticationMD5Password',
salt: Buffer.from([1, 2, 3, 4]),
};
var expectedSASLMessage = {
name: 'authenticationSASL',
mechanisms: ['SCRAM-SHA-256'],
};
var expectedSASLContinueMessage = {
name: 'authenticationSASLContinue',
data: 'data',
};
var expectedSASLFinalMessage = {
name: 'authenticationSASLFinal',
data: 'data',
};
var notificationResponseBuffer = test_buffers_1.default.notification(4, 'hi', 'boom');
var expectedNotificationResponseMessage = {
name: 'notification',
processId: 4,
channel: 'hi',
payload: 'boom',
};
const parseBuffers = (buffers) => __awaiter(void 0, void 0, void 0, function* () {
const stream = new stream_1.PassThrough();
for (const buffer of buffers) {
stream.write(buffer);
}
stream.end();
const msgs = [];
yield (0, _1.parse)(stream, (msg) => msgs.push(msg));
return msgs;
});
describe('PgPacketStream', function () {
testForMessage(authOkBuffer, expectedAuthenticationOkayMessage);
testForMessage(plainPasswordBuffer, expectedPlainPasswordMessage);
testForMessage(md5PasswordBuffer, expectedMD5PasswordMessage);
testForMessage(SASLBuffer, expectedSASLMessage);
testForMessage(SASLContinueBuffer, expectedSASLContinueMessage);
// this exercises a found bug in the parser:
// https://github.com/brianc/node-postgres/pull/2210#issuecomment-627626084
// and adds a test which is deterministic, rather than relying on network packet chunking
const extendedSASLContinueBuffer = Buffer.concat([SASLContinueBuffer, Buffer.from([1, 2, 3, 4])]);
testForMessage(extendedSASLContinueBuffer, expectedSASLContinueMessage);
testForMessage(SASLFinalBuffer, expectedSASLFinalMessage);
// this exercises a found bug in the parser:
// https://github.com/brianc/node-postgres/pull/2210#issuecomment-627626084
// and adds a test which is deterministic, rather than relying on network packet chunking
const extendedSASLFinalBuffer = Buffer.concat([SASLFinalBuffer, Buffer.from([1, 2, 4, 5])]);
testForMessage(extendedSASLFinalBuffer, expectedSASLFinalMessage);
testForMessage(paramStatusBuffer, expectedParameterStatusMessage);
testForMessage(backendKeyDataBuffer, expectedBackendKeyDataMessage);
testForMessage(readyForQueryBuffer, expectedReadyForQueryMessage);
testForMessage(commandCompleteBuffer, expectedCommandCompleteMessage);
testForMessage(notificationResponseBuffer, expectedNotificationResponseMessage);
testForMessage(test_buffers_1.default.emptyQuery(), {
name: 'emptyQuery',
length: 4,
});
testForMessage(Buffer.from([0x6e, 0, 0, 0, 4]), {
name: 'noData',
});
describe('rowDescription messages', function () {
testForMessage(emptyRowDescriptionBuffer, expectedEmptyRowDescriptionMessage);
testForMessage(oneRowDescBuff, expectedOneRowMessage);
testForMessage(twoRowBuf, expectedTwoRowMessage);
});
describe('parameterDescription messages', function () {
testForMessage(emptyParameterDescriptionBuffer, expectedEmptyParameterDescriptionMessage);
testForMessage(oneParameterDescBuf, expectedOneParameterMessage);
testForMessage(twoParameterDescBuf, expectedTwoParameterMessage);
});
describe('parsing rows', function () {
describe('parsing empty row', function () {
testForMessage(emptyRowFieldBuf, {
name: 'dataRow',
fieldCount: 0,
});
});
describe('parsing data row with fields', function () {
testForMessage(oneFieldBuf, {
name: 'dataRow',
fieldCount: 1,
fields: ['test'],
});
});
});
describe('notice message', function () {
// this uses the same logic as error message
var buff = test_buffers_1.default.notice([{ type: 'C', value: 'code' }]);
testForMessage(buff, {
name: 'notice',
code: 'code',
});
});
testForMessage(test_buffers_1.default.error([]), {
name: 'error',
});
describe('with all the fields', function () {
var buffer = test_buffers_1.default.error([
{
type: 'S',
value: 'ERROR',
},
{
type: 'C',
value: 'code',
},
{
type: 'M',
value: 'message',
},
{
type: 'D',
value: 'details',
},
{
type: 'H',
value: 'hint',
},
{
type: 'P',
value: '100',
},
{
type: 'p',
value: '101',
},
{
type: 'q',
value: 'query',
},
{
type: 'W',
value: 'where',
},
{
type: 'F',
value: 'file',
},
{
type: 'L',
value: 'line',
},
{
type: 'R',
value: 'routine',
},
{
type: 'Z',
value: 'alsdkf',
},
]);
testForMessage(buffer, {
name: 'error',
severity: 'ERROR',
code: 'code',
message: 'message',
detail: 'details',
hint: 'hint',
position: '100',
internalPosition: '101',
internalQuery: 'query',
where: 'where',
file: 'file',
line: 'line',
routine: 'routine',
});
});
testForMessage(parseCompleteBuffer, {
name: 'parseComplete',
});
testForMessage(bindCompleteBuffer, {
name: 'bindComplete',
});
testForMessage(bindCompleteBuffer, {
name: 'bindComplete',
});
testForMessage(test_buffers_1.default.closeComplete(), {
name: 'closeComplete',
});
describe('parses portal suspended message', function () {
testForMessage(portalSuspendedBuffer, {
name: 'portalSuspended',
});
});
describe('parses replication start message', function () {
testForMessage(Buffer.from([0x57, 0x00, 0x00, 0x00, 0x04]), {
name: 'replicationStart',
length: 4,
});
});
describe('copy', () => {
testForMessage(test_buffers_1.default.copyIn(0), {
name: 'copyInResponse',
length: 7,
binary: false,
columnTypes: [],
});
testForMessage(test_buffers_1.default.copyIn(2), {
name: 'copyInResponse',
length: 11,
binary: false,
columnTypes: [0, 1],
});
testForMessage(test_buffers_1.default.copyOut(0), {
name: 'copyOutResponse',
length: 7,
binary: false,
columnTypes: [],
});
testForMessage(test_buffers_1.default.copyOut(3), {
name: 'copyOutResponse',
length: 13,
binary: false,
columnTypes: [0, 1, 2],
});
testForMessage(test_buffers_1.default.copyDone(), {
name: 'copyDone',
length: 4,
});
testForMessage(test_buffers_1.default.copyData(Buffer.from([5, 6, 7])), {
name: 'copyData',
length: 7,
chunk: Buffer.from([5, 6, 7]),
});
});
// since the data message on a stream can randomly divide the incomming
// tcp packets anywhere, we need to make sure we can parse every single
// split on a tcp message
describe('split buffer, single message parsing', function () {
var fullBuffer = test_buffers_1.default.dataRow([null, 'bang', 'zug zug', null, '!']);
it('parses when full buffer comes in', function () {
return __awaiter(this, void 0, void 0, function* () {
const messages = yield parseBuffers([fullBuffer]);
const message = messages[0];
assert_1.default.equal(message.fields.length, 5);
assert_1.default.equal(message.fields[0], null);
assert_1.default.equal(message.fields[1], 'bang');
assert_1.default.equal(message.fields[2], 'zug zug');
assert_1.default.equal(message.fields[3], null);
assert_1.default.equal(message.fields[4], '!');
});
});
var testMessageRecievedAfterSpiltAt = function (split) {
return __awaiter(this, void 0, void 0, function* () {
var firstBuffer = Buffer.alloc(fullBuffer.length - split);
var secondBuffer = Buffer.alloc(fullBuffer.length - firstBuffer.length);
fullBuffer.copy(firstBuffer, 0, 0);
fullBuffer.copy(secondBuffer, 0, firstBuffer.length);
const messages = yield parseBuffers([fullBuffer]);
const message = messages[0];
assert_1.default.equal(message.fields.length, 5);
assert_1.default.equal(message.fields[0], null);
assert_1.default.equal(message.fields[1], 'bang');
assert_1.default.equal(message.fields[2], 'zug zug');
assert_1.default.equal(message.fields[3], null);
assert_1.default.equal(message.fields[4], '!');
});
};
it('parses when split in the middle', function () {
testMessageRecievedAfterSpiltAt(6);
});
it('parses when split at end', function () {
testMessageRecievedAfterSpiltAt(2);
});
it('parses when split at beginning', function () {
testMessageRecievedAfterSpiltAt(fullBuffer.length - 2);
testMessageRecievedAfterSpiltAt(fullBuffer.length - 1);
testMessageRecievedAfterSpiltAt(fullBuffer.length - 5);
});
});
describe('split buffer, multiple message parsing', function () {
var dataRowBuffer = test_buffers_1.default.dataRow(['!']);
var readyForQueryBuffer = test_buffers_1.default.readyForQuery();
var fullBuffer = Buffer.alloc(dataRowBuffer.length + readyForQueryBuffer.length);
dataRowBuffer.copy(fullBuffer, 0, 0);
readyForQueryBuffer.copy(fullBuffer, dataRowBuffer.length, 0);
var verifyMessages = function (messages) {
assert_1.default.strictEqual(messages.length, 2);
assert_1.default.deepEqual(messages[0], {
name: 'dataRow',
fieldCount: 1,
length: 11,
fields: ['!'],
});
assert_1.default.equal(messages[0].fields[0], '!');
assert_1.default.deepEqual(messages[1], {
name: 'readyForQuery',
length: 5,
status: 'I',
});
};
// sanity check
it('recieves both messages when packet is not split', function () {
return __awaiter(this, void 0, void 0, function* () {
const messages = yield parseBuffers([fullBuffer]);
verifyMessages(messages);
});
});
var splitAndVerifyTwoMessages = function (split) {
return __awaiter(this, void 0, void 0, function* () {
var firstBuffer = Buffer.alloc(fullBuffer.length - split);
var secondBuffer = Buffer.alloc(fullBuffer.length - firstBuffer.length);
fullBuffer.copy(firstBuffer, 0, 0);
fullBuffer.copy(secondBuffer, 0, firstBuffer.length);
const messages = yield parseBuffers([firstBuffer, secondBuffer]);
verifyMessages(messages);
});
};
describe('recieves both messages when packet is split', function () {
it('in the middle', function () {
return splitAndVerifyTwoMessages(11);
});
it('at the front', function () {
return Promise.all([
splitAndVerifyTwoMessages(fullBuffer.length - 1),
splitAndVerifyTwoMessages(fullBuffer.length - 4),
splitAndVerifyTwoMessages(fullBuffer.length - 6),
]);
});
it('at the end', function () {
return Promise.all([splitAndVerifyTwoMessages(8), splitAndVerifyTwoMessages(1)]);
});
});
});
});
//# sourceMappingURL=inbound-parser.test.js.map

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,6 @@
/// <reference types="node" />
import { DatabaseError } from './messages';
import { serialize } from './serializer';
import { MessageCallback } from './parser';
export declare function parse(stream: NodeJS.ReadableStream, callback: MessageCallback): Promise<void>;
export { serialize, DatabaseError };

View File

@ -0,0 +1,15 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.DatabaseError = exports.serialize = exports.parse = void 0;
const messages_1 = require("./messages");
Object.defineProperty(exports, "DatabaseError", { enumerable: true, get: function () { return messages_1.DatabaseError; } });
const serializer_1 = require("./serializer");
Object.defineProperty(exports, "serialize", { enumerable: true, get: function () { return serializer_1.serialize; } });
const parser_1 = require("./parser");
function parse(stream, callback) {
const parser = new parser_1.Parser();
stream.on('data', (buffer) => parser.parse(buffer, callback));
return new Promise((resolve) => stream.on('end', () => resolve()));
}
exports.parse = parse;
//# sourceMappingURL=index.js.map

View File

@ -0,0 +1 @@
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,yCAA0C;AAUtB,8FAVX,wBAAa,OAUW;AATjC,6CAAwC;AAS/B,0FATA,sBAAS,OASA;AARlB,qCAAkD;AAElD,SAAgB,KAAK,CAAC,MAA6B,EAAE,QAAyB;IAC5E,MAAM,MAAM,GAAG,IAAI,eAAM,EAAE,CAAA;IAC3B,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,MAAc,EAAE,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAA;IACrE,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC,CAAA;AACpE,CAAC;AAJD,sBAIC"}

View File

@ -0,0 +1,162 @@
/// <reference types="node" />
export declare type Mode = 'text' | 'binary';
export declare type MessageName = 'parseComplete' | 'bindComplete' | 'closeComplete' | 'noData' | 'portalSuspended' | 'replicationStart' | 'emptyQuery' | 'copyDone' | 'copyData' | 'rowDescription' | 'parameterDescription' | 'parameterStatus' | 'backendKeyData' | 'notification' | 'readyForQuery' | 'commandComplete' | 'dataRow' | 'copyInResponse' | 'copyOutResponse' | 'authenticationOk' | 'authenticationMD5Password' | 'authenticationCleartextPassword' | 'authenticationSASL' | 'authenticationSASLContinue' | 'authenticationSASLFinal' | 'error' | 'notice';
export interface BackendMessage {
name: MessageName;
length: number;
}
export declare const parseComplete: BackendMessage;
export declare const bindComplete: BackendMessage;
export declare const closeComplete: BackendMessage;
export declare const noData: BackendMessage;
export declare const portalSuspended: BackendMessage;
export declare const replicationStart: BackendMessage;
export declare const emptyQuery: BackendMessage;
export declare const copyDone: BackendMessage;
interface NoticeOrError {
message: string | undefined;
severity: string | undefined;
code: string | undefined;
detail: string | undefined;
hint: string | undefined;
position: string | undefined;
internalPosition: string | undefined;
internalQuery: string | undefined;
where: string | undefined;
schema: string | undefined;
table: string | undefined;
column: string | undefined;
dataType: string | undefined;
constraint: string | undefined;
file: string | undefined;
line: string | undefined;
routine: string | undefined;
}
export declare class DatabaseError extends Error implements NoticeOrError {
readonly length: number;
readonly name: MessageName;
severity: string | undefined;
code: string | undefined;
detail: string | undefined;
hint: string | undefined;
position: string | undefined;
internalPosition: string | undefined;
internalQuery: string | undefined;
where: string | undefined;
schema: string | undefined;
table: string | undefined;
column: string | undefined;
dataType: string | undefined;
constraint: string | undefined;
file: string | undefined;
line: string | undefined;
routine: string | undefined;
constructor(message: string, length: number, name: MessageName);
}
export declare class CopyDataMessage {
readonly length: number;
readonly chunk: Buffer;
readonly name = "copyData";
constructor(length: number, chunk: Buffer);
}
export declare class CopyResponse {
readonly length: number;
readonly name: MessageName;
readonly binary: boolean;
readonly columnTypes: number[];
constructor(length: number, name: MessageName, binary: boolean, columnCount: number);
}
export declare class Field {
readonly name: string;
readonly tableID: number;
readonly columnID: number;
readonly dataTypeID: number;
readonly dataTypeSize: number;
readonly dataTypeModifier: number;
readonly format: Mode;
constructor(name: string, tableID: number, columnID: number, dataTypeID: number, dataTypeSize: number, dataTypeModifier: number, format: Mode);
}
export declare class RowDescriptionMessage {
readonly length: number;
readonly fieldCount: number;
readonly name: MessageName;
readonly fields: Field[];
constructor(length: number, fieldCount: number);
}
export declare class ParameterDescriptionMessage {
readonly length: number;
readonly parameterCount: number;
readonly name: MessageName;
readonly dataTypeIDs: number[];
constructor(length: number, parameterCount: number);
}
export declare class ParameterStatusMessage {
readonly length: number;
readonly parameterName: string;
readonly parameterValue: string;
readonly name: MessageName;
constructor(length: number, parameterName: string, parameterValue: string);
}
export declare class AuthenticationMD5Password implements BackendMessage {
readonly length: number;
readonly salt: Buffer;
readonly name: MessageName;
constructor(length: number, salt: Buffer);
}
export declare class BackendKeyDataMessage {
readonly length: number;
readonly processID: number;
readonly secretKey: number;
readonly name: MessageName;
constructor(length: number, processID: number, secretKey: number);
}
export declare class NotificationResponseMessage {
readonly length: number;
readonly processId: number;
readonly channel: string;
readonly payload: string;
readonly name: MessageName;
constructor(length: number, processId: number, channel: string, payload: string);
}
export declare class ReadyForQueryMessage {
readonly length: number;
readonly status: string;
readonly name: MessageName;
constructor(length: number, status: string);
}
export declare class CommandCompleteMessage {
readonly length: number;
readonly text: string;
readonly name: MessageName;
constructor(length: number, text: string);
}
export declare class DataRowMessage {
length: number;
fields: any[];
readonly fieldCount: number;
readonly name: MessageName;
constructor(length: number, fields: any[]);
}
export declare class NoticeMessage implements BackendMessage, NoticeOrError {
readonly length: number;
readonly message: string | undefined;
constructor(length: number, message: string | undefined);
readonly name = "notice";
severity: string | undefined;
code: string | undefined;
detail: string | undefined;
hint: string | undefined;
position: string | undefined;
internalPosition: string | undefined;
internalQuery: string | undefined;
where: string | undefined;
schema: string | undefined;
table: string | undefined;
column: string | undefined;
dataType: string | undefined;
constraint: string | undefined;
file: string | undefined;
line: string | undefined;
routine: string | undefined;
}
export {};

View File

@ -0,0 +1,160 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.NoticeMessage = exports.DataRowMessage = exports.CommandCompleteMessage = exports.ReadyForQueryMessage = exports.NotificationResponseMessage = exports.BackendKeyDataMessage = exports.AuthenticationMD5Password = exports.ParameterStatusMessage = exports.ParameterDescriptionMessage = exports.RowDescriptionMessage = exports.Field = exports.CopyResponse = exports.CopyDataMessage = exports.DatabaseError = exports.copyDone = exports.emptyQuery = exports.replicationStart = exports.portalSuspended = exports.noData = exports.closeComplete = exports.bindComplete = exports.parseComplete = void 0;
exports.parseComplete = {
name: 'parseComplete',
length: 5,
};
exports.bindComplete = {
name: 'bindComplete',
length: 5,
};
exports.closeComplete = {
name: 'closeComplete',
length: 5,
};
exports.noData = {
name: 'noData',
length: 5,
};
exports.portalSuspended = {
name: 'portalSuspended',
length: 5,
};
exports.replicationStart = {
name: 'replicationStart',
length: 4,
};
exports.emptyQuery = {
name: 'emptyQuery',
length: 4,
};
exports.copyDone = {
name: 'copyDone',
length: 4,
};
class DatabaseError extends Error {
constructor(message, length, name) {
super(message);
this.length = length;
this.name = name;
}
}
exports.DatabaseError = DatabaseError;
class CopyDataMessage {
constructor(length, chunk) {
this.length = length;
this.chunk = chunk;
this.name = 'copyData';
}
}
exports.CopyDataMessage = CopyDataMessage;
class CopyResponse {
constructor(length, name, binary, columnCount) {
this.length = length;
this.name = name;
this.binary = binary;
this.columnTypes = new Array(columnCount);
}
}
exports.CopyResponse = CopyResponse;
class Field {
constructor(name, tableID, columnID, dataTypeID, dataTypeSize, dataTypeModifier, format) {
this.name = name;
this.tableID = tableID;
this.columnID = columnID;
this.dataTypeID = dataTypeID;
this.dataTypeSize = dataTypeSize;
this.dataTypeModifier = dataTypeModifier;
this.format = format;
}
}
exports.Field = Field;
class RowDescriptionMessage {
constructor(length, fieldCount) {
this.length = length;
this.fieldCount = fieldCount;
this.name = 'rowDescription';
this.fields = new Array(this.fieldCount);
}
}
exports.RowDescriptionMessage = RowDescriptionMessage;
class ParameterDescriptionMessage {
constructor(length, parameterCount) {
this.length = length;
this.parameterCount = parameterCount;
this.name = 'parameterDescription';
this.dataTypeIDs = new Array(this.parameterCount);
}
}
exports.ParameterDescriptionMessage = ParameterDescriptionMessage;
class ParameterStatusMessage {
constructor(length, parameterName, parameterValue) {
this.length = length;
this.parameterName = parameterName;
this.parameterValue = parameterValue;
this.name = 'parameterStatus';
}
}
exports.ParameterStatusMessage = ParameterStatusMessage;
class AuthenticationMD5Password {
constructor(length, salt) {
this.length = length;
this.salt = salt;
this.name = 'authenticationMD5Password';
}
}
exports.AuthenticationMD5Password = AuthenticationMD5Password;
class BackendKeyDataMessage {
constructor(length, processID, secretKey) {
this.length = length;
this.processID = processID;
this.secretKey = secretKey;
this.name = 'backendKeyData';
}
}
exports.BackendKeyDataMessage = BackendKeyDataMessage;
class NotificationResponseMessage {
constructor(length, processId, channel, payload) {
this.length = length;
this.processId = processId;
this.channel = channel;
this.payload = payload;
this.name = 'notification';
}
}
exports.NotificationResponseMessage = NotificationResponseMessage;
class ReadyForQueryMessage {
constructor(length, status) {
this.length = length;
this.status = status;
this.name = 'readyForQuery';
}
}
exports.ReadyForQueryMessage = ReadyForQueryMessage;
class CommandCompleteMessage {
constructor(length, text) {
this.length = length;
this.text = text;
this.name = 'commandComplete';
}
}
exports.CommandCompleteMessage = CommandCompleteMessage;
class DataRowMessage {
constructor(length, fields) {
this.length = length;
this.fields = fields;
this.name = 'dataRow';
this.fieldCount = fields.length;
}
}
exports.DataRowMessage = DataRowMessage;
class NoticeMessage {
constructor(length, message) {
this.length = length;
this.message = message;
this.name = 'notice';
}
}
exports.NoticeMessage = NoticeMessage;
//# sourceMappingURL=messages.js.map

View File

@ -0,0 +1 @@
{"version":3,"file":"messages.js","sourceRoot":"","sources":["../src/messages.ts"],"names":[],"mappings":"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"}

View File

@ -0,0 +1 @@
export {};

View File

@ -0,0 +1,248 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const assert_1 = __importDefault(require("assert"));
const serializer_1 = require("./serializer");
const buffer_list_1 = __importDefault(require("./testing/buffer-list"));
describe('serializer', () => {
it('builds startup message', function () {
const actual = serializer_1.serialize.startup({
user: 'brian',
database: 'bang',
});
assert_1.default.deepEqual(actual, new buffer_list_1.default()
.addInt16(3)
.addInt16(0)
.addCString('user')
.addCString('brian')
.addCString('database')
.addCString('bang')
.addCString('client_encoding')
.addCString('UTF8')
.addCString('')
.join(true));
});
it('builds password message', function () {
const actual = serializer_1.serialize.password('!');
assert_1.default.deepEqual(actual, new buffer_list_1.default().addCString('!').join(true, 'p'));
});
it('builds request ssl message', function () {
const actual = serializer_1.serialize.requestSsl();
const expected = new buffer_list_1.default().addInt32(80877103).join(true);
assert_1.default.deepEqual(actual, expected);
});
it('builds SASLInitialResponseMessage message', function () {
const actual = serializer_1.serialize.sendSASLInitialResponseMessage('mech', 'data');
assert_1.default.deepEqual(actual, new buffer_list_1.default().addCString('mech').addInt32(4).addString('data').join(true, 'p'));
});
it('builds SCRAMClientFinalMessage message', function () {
const actual = serializer_1.serialize.sendSCRAMClientFinalMessage('data');
assert_1.default.deepEqual(actual, new buffer_list_1.default().addString('data').join(true, 'p'));
});
it('builds query message', function () {
var txt = 'select * from boom';
const actual = serializer_1.serialize.query(txt);
assert_1.default.deepEqual(actual, new buffer_list_1.default().addCString(txt).join(true, 'Q'));
});
describe('parse message', () => {
it('builds parse message', function () {
const actual = serializer_1.serialize.parse({ text: '!' });
var expected = new buffer_list_1.default().addCString('').addCString('!').addInt16(0).join(true, 'P');
assert_1.default.deepEqual(actual, expected);
});
it('builds parse message with named query', function () {
const actual = serializer_1.serialize.parse({
name: 'boom',
text: 'select * from boom',
types: [],
});
var expected = new buffer_list_1.default().addCString('boom').addCString('select * from boom').addInt16(0).join(true, 'P');
assert_1.default.deepEqual(actual, expected);
});
it('with multiple parameters', function () {
const actual = serializer_1.serialize.parse({
name: 'force',
text: 'select * from bang where name = $1',
types: [1, 2, 3, 4],
});
var expected = new buffer_list_1.default()
.addCString('force')
.addCString('select * from bang where name = $1')
.addInt16(4)
.addInt32(1)
.addInt32(2)
.addInt32(3)
.addInt32(4)
.join(true, 'P');
assert_1.default.deepEqual(actual, expected);
});
});
describe('bind messages', function () {
it('with no values', function () {
const actual = serializer_1.serialize.bind();
var expectedBuffer = new buffer_list_1.default()
.addCString('')
.addCString('')
.addInt16(0)
.addInt16(0)
.addInt16(0)
.join(true, 'B');
assert_1.default.deepEqual(actual, expectedBuffer);
});
it('with named statement, portal, and values', function () {
const actual = serializer_1.serialize.bind({
portal: 'bang',
statement: 'woo',
values: ['1', 'hi', null, 'zing'],
});
var expectedBuffer = new buffer_list_1.default()
.addCString('bang') // portal name
.addCString('woo') // statement name
.addInt16(4)
.addInt16(0)
.addInt16(0)
.addInt16(0)
.addInt16(0)
.addInt16(4)
.addInt32(1)
.add(Buffer.from('1'))
.addInt32(2)
.add(Buffer.from('hi'))
.addInt32(-1)
.addInt32(4)
.add(Buffer.from('zing'))
.addInt16(0)
.join(true, 'B');
assert_1.default.deepEqual(actual, expectedBuffer);
});
});
it('with custom valueMapper', function () {
const actual = serializer_1.serialize.bind({
portal: 'bang',
statement: 'woo',
values: ['1', 'hi', null, 'zing'],
valueMapper: () => null,
});
var expectedBuffer = new buffer_list_1.default()
.addCString('bang') // portal name
.addCString('woo') // statement name
.addInt16(4)
.addInt16(0)
.addInt16(0)
.addInt16(0)
.addInt16(0)
.addInt16(4)
.addInt32(-1)
.addInt32(-1)
.addInt32(-1)
.addInt32(-1)
.addInt16(0)
.join(true, 'B');
assert_1.default.deepEqual(actual, expectedBuffer);
});
it('with named statement, portal, and buffer value', function () {
const actual = serializer_1.serialize.bind({
portal: 'bang',
statement: 'woo',
values: ['1', 'hi', null, Buffer.from('zing', 'utf8')],
});
var expectedBuffer = new buffer_list_1.default()
.addCString('bang') // portal name
.addCString('woo') // statement name
.addInt16(4) // value count
.addInt16(0) // string
.addInt16(0) // string
.addInt16(0) // string
.addInt16(1) // binary
.addInt16(4)
.addInt32(1)
.add(Buffer.from('1'))
.addInt32(2)
.add(Buffer.from('hi'))
.addInt32(-1)
.addInt32(4)
.add(Buffer.from('zing', 'utf-8'))
.addInt16(0)
.join(true, 'B');
assert_1.default.deepEqual(actual, expectedBuffer);
});
describe('builds execute message', function () {
it('for unamed portal with no row limit', function () {
const actual = serializer_1.serialize.execute();
var expectedBuffer = new buffer_list_1.default().addCString('').addInt32(0).join(true, 'E');
assert_1.default.deepEqual(actual, expectedBuffer);
});
it('for named portal with row limit', function () {
const actual = serializer_1.serialize.execute({
portal: 'my favorite portal',
rows: 100,
});
var expectedBuffer = new buffer_list_1.default().addCString('my favorite portal').addInt32(100).join(true, 'E');
assert_1.default.deepEqual(actual, expectedBuffer);
});
});
it('builds flush command', function () {
const actual = serializer_1.serialize.flush();
var expected = new buffer_list_1.default().join(true, 'H');
assert_1.default.deepEqual(actual, expected);
});
it('builds sync command', function () {
const actual = serializer_1.serialize.sync();
var expected = new buffer_list_1.default().join(true, 'S');
assert_1.default.deepEqual(actual, expected);
});
it('builds end command', function () {
const actual = serializer_1.serialize.end();
var expected = Buffer.from([0x58, 0, 0, 0, 4]);
assert_1.default.deepEqual(actual, expected);
});
describe('builds describe command', function () {
it('describe statement', function () {
const actual = serializer_1.serialize.describe({ type: 'S', name: 'bang' });
var expected = new buffer_list_1.default().addChar('S').addCString('bang').join(true, 'D');
assert_1.default.deepEqual(actual, expected);
});
it('describe unnamed portal', function () {
const actual = serializer_1.serialize.describe({ type: 'P' });
var expected = new buffer_list_1.default().addChar('P').addCString('').join(true, 'D');
assert_1.default.deepEqual(actual, expected);
});
});
describe('builds close command', function () {
it('describe statement', function () {
const actual = serializer_1.serialize.close({ type: 'S', name: 'bang' });
var expected = new buffer_list_1.default().addChar('S').addCString('bang').join(true, 'C');
assert_1.default.deepEqual(actual, expected);
});
it('describe unnamed portal', function () {
const actual = serializer_1.serialize.close({ type: 'P' });
var expected = new buffer_list_1.default().addChar('P').addCString('').join(true, 'C');
assert_1.default.deepEqual(actual, expected);
});
});
describe('copy messages', function () {
it('builds copyFromChunk', () => {
const actual = serializer_1.serialize.copyData(Buffer.from([1, 2, 3]));
const expected = new buffer_list_1.default().add(Buffer.from([1, 2, 3])).join(true, 'd');
assert_1.default.deepEqual(actual, expected);
});
it('builds copy fail', () => {
const actual = serializer_1.serialize.copyFail('err!');
const expected = new buffer_list_1.default().addCString('err!').join(true, 'f');
assert_1.default.deepEqual(actual, expected);
});
it('builds copy done', () => {
const actual = serializer_1.serialize.copyDone();
const expected = new buffer_list_1.default().join(true, 'c');
assert_1.default.deepEqual(actual, expected);
});
});
it('builds cancel message', () => {
const actual = serializer_1.serialize.cancel(3, 4);
const expected = new buffer_list_1.default().addInt16(1234).addInt16(5678).addInt32(3).addInt32(4).join(true);
assert_1.default.deepEqual(actual, expected);
});
});
//# sourceMappingURL=outbound-serializer.test.js.map

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,39 @@
/// <reference types="node" />
/// <reference types="node" />
import { TransformOptions } from 'stream';
import { Mode, BackendMessage } from './messages';
export declare type Packet = {
code: number;
packet: Buffer;
};
declare type StreamOptions = TransformOptions & {
mode: Mode;
};
export declare type MessageCallback = (msg: BackendMessage) => void;
export declare class Parser {
private buffer;
private bufferLength;
private bufferOffset;
private reader;
private mode;
constructor(opts?: StreamOptions);
parse(buffer: Buffer, callback: MessageCallback): void;
private mergeBuffer;
private handlePacket;
private parseReadyForQueryMessage;
private parseCommandCompleteMessage;
private parseCopyData;
private parseCopyInMessage;
private parseCopyOutMessage;
private parseCopyMessage;
private parseNotificationMessage;
private parseRowDescriptionMessage;
private parseField;
private parseParameterDescriptionMessage;
private parseDataRowMessage;
private parseParameterStatusMessage;
private parseBackendKeyData;
parseAuthenticationResponse(offset: number, length: number, bytes: Buffer): any;
private parseErrorMessage;
}
export {};

View File

@ -0,0 +1,304 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Parser = void 0;
const messages_1 = require("./messages");
const buffer_reader_1 = require("./buffer-reader");
// every message is prefixed with a single bye
const CODE_LENGTH = 1;
// every message has an int32 length which includes itself but does
// NOT include the code in the length
const LEN_LENGTH = 4;
const HEADER_LENGTH = CODE_LENGTH + LEN_LENGTH;
const emptyBuffer = Buffer.allocUnsafe(0);
class Parser {
constructor(opts) {
this.buffer = emptyBuffer;
this.bufferLength = 0;
this.bufferOffset = 0;
this.reader = new buffer_reader_1.BufferReader();
if ((opts === null || opts === void 0 ? void 0 : opts.mode) === 'binary') {
throw new Error('Binary mode not supported yet');
}
this.mode = (opts === null || opts === void 0 ? void 0 : opts.mode) || 'text';
}
parse(buffer, callback) {
this.mergeBuffer(buffer);
const bufferFullLength = this.bufferOffset + this.bufferLength;
let offset = this.bufferOffset;
while (offset + HEADER_LENGTH <= bufferFullLength) {
// code is 1 byte long - it identifies the message type
const code = this.buffer[offset];
// length is 1 Uint32BE - it is the length of the message EXCLUDING the code
const length = this.buffer.readUInt32BE(offset + CODE_LENGTH);
const fullMessageLength = CODE_LENGTH + length;
if (fullMessageLength + offset <= bufferFullLength) {
const message = this.handlePacket(offset + HEADER_LENGTH, code, length, this.buffer);
callback(message);
offset += fullMessageLength;
}
else {
break;
}
}
if (offset === bufferFullLength) {
// No more use for the buffer
this.buffer = emptyBuffer;
this.bufferLength = 0;
this.bufferOffset = 0;
}
else {
// Adjust the cursors of remainingBuffer
this.bufferLength = bufferFullLength - offset;
this.bufferOffset = offset;
}
}
mergeBuffer(buffer) {
if (this.bufferLength > 0) {
const newLength = this.bufferLength + buffer.byteLength;
const newFullLength = newLength + this.bufferOffset;
if (newFullLength > this.buffer.byteLength) {
// We can't concat the new buffer with the remaining one
let newBuffer;
if (newLength <= this.buffer.byteLength && this.bufferOffset >= this.bufferLength) {
// We can move the relevant part to the beginning of the buffer instead of allocating a new buffer
newBuffer = this.buffer;
}
else {
// Allocate a new larger buffer
let newBufferLength = this.buffer.byteLength * 2;
while (newLength >= newBufferLength) {
newBufferLength *= 2;
}
newBuffer = Buffer.allocUnsafe(newBufferLength);
}
// Move the remaining buffer to the new one
this.buffer.copy(newBuffer, 0, this.bufferOffset, this.bufferOffset + this.bufferLength);
this.buffer = newBuffer;
this.bufferOffset = 0;
}
// Concat the new buffer with the remaining one
buffer.copy(this.buffer, this.bufferOffset + this.bufferLength);
this.bufferLength = newLength;
}
else {
this.buffer = buffer;
this.bufferOffset = 0;
this.bufferLength = buffer.byteLength;
}
}
handlePacket(offset, code, length, bytes) {
switch (code) {
case 50 /* MessageCodes.BindComplete */:
return messages_1.bindComplete;
case 49 /* MessageCodes.ParseComplete */:
return messages_1.parseComplete;
case 51 /* MessageCodes.CloseComplete */:
return messages_1.closeComplete;
case 110 /* MessageCodes.NoData */:
return messages_1.noData;
case 115 /* MessageCodes.PortalSuspended */:
return messages_1.portalSuspended;
case 99 /* MessageCodes.CopyDone */:
return messages_1.copyDone;
case 87 /* MessageCodes.ReplicationStart */:
return messages_1.replicationStart;
case 73 /* MessageCodes.EmptyQuery */:
return messages_1.emptyQuery;
case 68 /* MessageCodes.DataRow */:
return this.parseDataRowMessage(offset, length, bytes);
case 67 /* MessageCodes.CommandComplete */:
return this.parseCommandCompleteMessage(offset, length, bytes);
case 90 /* MessageCodes.ReadyForQuery */:
return this.parseReadyForQueryMessage(offset, length, bytes);
case 65 /* MessageCodes.NotificationResponse */:
return this.parseNotificationMessage(offset, length, bytes);
case 82 /* MessageCodes.AuthenticationResponse */:
return this.parseAuthenticationResponse(offset, length, bytes);
case 83 /* MessageCodes.ParameterStatus */:
return this.parseParameterStatusMessage(offset, length, bytes);
case 75 /* MessageCodes.BackendKeyData */:
return this.parseBackendKeyData(offset, length, bytes);
case 69 /* MessageCodes.ErrorMessage */:
return this.parseErrorMessage(offset, length, bytes, 'error');
case 78 /* MessageCodes.NoticeMessage */:
return this.parseErrorMessage(offset, length, bytes, 'notice');
case 84 /* MessageCodes.RowDescriptionMessage */:
return this.parseRowDescriptionMessage(offset, length, bytes);
case 116 /* MessageCodes.ParameterDescriptionMessage */:
return this.parseParameterDescriptionMessage(offset, length, bytes);
case 71 /* MessageCodes.CopyIn */:
return this.parseCopyInMessage(offset, length, bytes);
case 72 /* MessageCodes.CopyOut */:
return this.parseCopyOutMessage(offset, length, bytes);
case 100 /* MessageCodes.CopyData */:
return this.parseCopyData(offset, length, bytes);
default:
return new messages_1.DatabaseError('received invalid response: ' + code.toString(16), length, 'error');
}
}
parseReadyForQueryMessage(offset, length, bytes) {
this.reader.setBuffer(offset, bytes);
const status = this.reader.string(1);
return new messages_1.ReadyForQueryMessage(length, status);
}
parseCommandCompleteMessage(offset, length, bytes) {
this.reader.setBuffer(offset, bytes);
const text = this.reader.cstring();
return new messages_1.CommandCompleteMessage(length, text);
}
parseCopyData(offset, length, bytes) {
const chunk = bytes.slice(offset, offset + (length - 4));
return new messages_1.CopyDataMessage(length, chunk);
}
parseCopyInMessage(offset, length, bytes) {
return this.parseCopyMessage(offset, length, bytes, 'copyInResponse');
}
parseCopyOutMessage(offset, length, bytes) {
return this.parseCopyMessage(offset, length, bytes, 'copyOutResponse');
}
parseCopyMessage(offset, length, bytes, messageName) {
this.reader.setBuffer(offset, bytes);
const isBinary = this.reader.byte() !== 0;
const columnCount = this.reader.int16();
const message = new messages_1.CopyResponse(length, messageName, isBinary, columnCount);
for (let i = 0; i < columnCount; i++) {
message.columnTypes[i] = this.reader.int16();
}
return message;
}
parseNotificationMessage(offset, length, bytes) {
this.reader.setBuffer(offset, bytes);
const processId = this.reader.int32();
const channel = this.reader.cstring();
const payload = this.reader.cstring();
return new messages_1.NotificationResponseMessage(length, processId, channel, payload);
}
parseRowDescriptionMessage(offset, length, bytes) {
this.reader.setBuffer(offset, bytes);
const fieldCount = this.reader.int16();
const message = new messages_1.RowDescriptionMessage(length, fieldCount);
for (let i = 0; i < fieldCount; i++) {
message.fields[i] = this.parseField();
}
return message;
}
parseField() {
const name = this.reader.cstring();
const tableID = this.reader.int32();
const columnID = this.reader.int16();
const dataTypeID = this.reader.int32();
const dataTypeSize = this.reader.int16();
const dataTypeModifier = this.reader.int32();
const mode = this.reader.int16() === 0 ? 'text' : 'binary';
return new messages_1.Field(name, tableID, columnID, dataTypeID, dataTypeSize, dataTypeModifier, mode);
}
parseParameterDescriptionMessage(offset, length, bytes) {
this.reader.setBuffer(offset, bytes);
const parameterCount = this.reader.int16();
const message = new messages_1.ParameterDescriptionMessage(length, parameterCount);
for (let i = 0; i < parameterCount; i++) {
message.dataTypeIDs[i] = this.reader.int32();
}
return message;
}
parseDataRowMessage(offset, length, bytes) {
this.reader.setBuffer(offset, bytes);
const fieldCount = this.reader.int16();
const fields = new Array(fieldCount);
for (let i = 0; i < fieldCount; i++) {
const len = this.reader.int32();
// a -1 for length means the value of the field is null
fields[i] = len === -1 ? null : this.reader.string(len);
}
return new messages_1.DataRowMessage(length, fields);
}
parseParameterStatusMessage(offset, length, bytes) {
this.reader.setBuffer(offset, bytes);
const name = this.reader.cstring();
const value = this.reader.cstring();
return new messages_1.ParameterStatusMessage(length, name, value);
}
parseBackendKeyData(offset, length, bytes) {
this.reader.setBuffer(offset, bytes);
const processID = this.reader.int32();
const secretKey = this.reader.int32();
return new messages_1.BackendKeyDataMessage(length, processID, secretKey);
}
parseAuthenticationResponse(offset, length, bytes) {
this.reader.setBuffer(offset, bytes);
const code = this.reader.int32();
// TODO(bmc): maybe better types here
const message = {
name: 'authenticationOk',
length,
};
switch (code) {
case 0: // AuthenticationOk
break;
case 3: // AuthenticationCleartextPassword
if (message.length === 8) {
message.name = 'authenticationCleartextPassword';
}
break;
case 5: // AuthenticationMD5Password
if (message.length === 12) {
message.name = 'authenticationMD5Password';
const salt = this.reader.bytes(4);
return new messages_1.AuthenticationMD5Password(length, salt);
}
break;
case 10: // AuthenticationSASL
message.name = 'authenticationSASL';
message.mechanisms = [];
let mechanism;
do {
mechanism = this.reader.cstring();
if (mechanism) {
message.mechanisms.push(mechanism);
}
} while (mechanism);
break;
case 11: // AuthenticationSASLContinue
message.name = 'authenticationSASLContinue';
message.data = this.reader.string(length - 8);
break;
case 12: // AuthenticationSASLFinal
message.name = 'authenticationSASLFinal';
message.data = this.reader.string(length - 8);
break;
default:
throw new Error('Unknown authenticationOk message type ' + code);
}
return message;
}
parseErrorMessage(offset, length, bytes, name) {
this.reader.setBuffer(offset, bytes);
const fields = {};
let fieldType = this.reader.string(1);
while (fieldType !== '\0') {
fields[fieldType] = this.reader.cstring();
fieldType = this.reader.string(1);
}
const messageValue = fields.M;
const message = name === 'notice' ? new messages_1.NoticeMessage(length, messageValue) : new messages_1.DatabaseError(messageValue, length, name);
message.severity = fields.S;
message.code = fields.C;
message.detail = fields.D;
message.hint = fields.H;
message.position = fields.P;
message.internalPosition = fields.p;
message.internalQuery = fields.q;
message.where = fields.W;
message.schema = fields.s;
message.table = fields.t;
message.column = fields.c;
message.dataType = fields.d;
message.constraint = fields.n;
message.file = fields.F;
message.line = fields.L;
message.routine = fields.R;
return message;
}
}
exports.Parser = Parser;
//# sourceMappingURL=parser.js.map

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,42 @@
declare type ParseOpts = {
name?: string;
types?: number[];
text: string;
};
declare type ValueMapper = (param: any, index: number) => any;
declare type BindOpts = {
portal?: string;
binary?: boolean;
statement?: string;
values?: any[];
valueMapper?: ValueMapper;
};
declare type ExecOpts = {
portal?: string;
rows?: number;
};
declare type PortalOpts = {
type: 'S' | 'P';
name?: string;
};
declare const serialize: {
startup: (opts: Record<string, string>) => Buffer;
password: (password: string) => Buffer;
requestSsl: () => Buffer;
sendSASLInitialResponseMessage: (mechanism: string, initialResponse: string) => Buffer;
sendSCRAMClientFinalMessage: (additionalData: string) => Buffer;
query: (text: string) => Buffer;
parse: (query: ParseOpts) => Buffer;
bind: (config?: BindOpts) => Buffer;
execute: (config?: ExecOpts) => Buffer;
describe: (msg: PortalOpts) => Buffer;
close: (msg: PortalOpts) => Buffer;
flush: () => Buffer;
sync: () => Buffer;
end: () => Buffer;
copyData: (chunk: Buffer) => Buffer;
copyDone: () => Buffer;
copyFail: (message: string) => Buffer;
cancel: (processID: number, secretKey: number) => Buffer;
};
export { serialize };

View File

@ -0,0 +1,189 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.serialize = void 0;
const buffer_writer_1 = require("./buffer-writer");
const writer = new buffer_writer_1.Writer();
const startup = (opts) => {
// protocol version
writer.addInt16(3).addInt16(0);
for (const key of Object.keys(opts)) {
writer.addCString(key).addCString(opts[key]);
}
writer.addCString('client_encoding').addCString('UTF8');
var bodyBuffer = writer.addCString('').flush();
// this message is sent without a code
var length = bodyBuffer.length + 4;
return new buffer_writer_1.Writer().addInt32(length).add(bodyBuffer).flush();
};
const requestSsl = () => {
const response = Buffer.allocUnsafe(8);
response.writeInt32BE(8, 0);
response.writeInt32BE(80877103, 4);
return response;
};
const password = (password) => {
return writer.addCString(password).flush(112 /* code.startup */);
};
const sendSASLInitialResponseMessage = function (mechanism, initialResponse) {
// 0x70 = 'p'
writer.addCString(mechanism).addInt32(Buffer.byteLength(initialResponse)).addString(initialResponse);
return writer.flush(112 /* code.startup */);
};
const sendSCRAMClientFinalMessage = function (additionalData) {
return writer.addString(additionalData).flush(112 /* code.startup */);
};
const query = (text) => {
return writer.addCString(text).flush(81 /* code.query */);
};
const emptyArray = [];
const parse = (query) => {
// expect something like this:
// { name: 'queryName',
// text: 'select * from blah',
// types: ['int8', 'bool'] }
// normalize missing query names to allow for null
const name = query.name || '';
if (name.length > 63) {
/* eslint-disable no-console */
console.error('Warning! Postgres only supports 63 characters for query names.');
console.error('You supplied %s (%s)', name, name.length);
console.error('This can cause conflicts and silent errors executing queries');
/* eslint-enable no-console */
}
const types = query.types || emptyArray;
var len = types.length;
var buffer = writer
.addCString(name) // name of query
.addCString(query.text) // actual query text
.addInt16(len);
for (var i = 0; i < len; i++) {
buffer.addInt32(types[i]);
}
return writer.flush(80 /* code.parse */);
};
const paramWriter = new buffer_writer_1.Writer();
const writeValues = function (values, valueMapper) {
for (let i = 0; i < values.length; i++) {
const mappedVal = valueMapper ? valueMapper(values[i], i) : values[i];
if (mappedVal == null) {
// add the param type (string) to the writer
writer.addInt16(0 /* ParamType.STRING */);
// write -1 to the param writer to indicate null
paramWriter.addInt32(-1);
}
else if (mappedVal instanceof Buffer) {
// add the param type (binary) to the writer
writer.addInt16(1 /* ParamType.BINARY */);
// add the buffer to the param writer
paramWriter.addInt32(mappedVal.length);
paramWriter.add(mappedVal);
}
else {
// add the param type (string) to the writer
writer.addInt16(0 /* ParamType.STRING */);
paramWriter.addInt32(Buffer.byteLength(mappedVal));
paramWriter.addString(mappedVal);
}
}
};
const bind = (config = {}) => {
// normalize config
const portal = config.portal || '';
const statement = config.statement || '';
const binary = config.binary || false;
const values = config.values || emptyArray;
const len = values.length;
writer.addCString(portal).addCString(statement);
writer.addInt16(len);
writeValues(values, config.valueMapper);
writer.addInt16(len);
writer.add(paramWriter.flush());
// format code
writer.addInt16(binary ? 1 /* ParamType.BINARY */ : 0 /* ParamType.STRING */);
return writer.flush(66 /* code.bind */);
};
const emptyExecute = Buffer.from([69 /* code.execute */, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00]);
const execute = (config) => {
// this is the happy path for most queries
if (!config || (!config.portal && !config.rows)) {
return emptyExecute;
}
const portal = config.portal || '';
const rows = config.rows || 0;
const portalLength = Buffer.byteLength(portal);
const len = 4 + portalLength + 1 + 4;
// one extra bit for code
const buff = Buffer.allocUnsafe(1 + len);
buff[0] = 69 /* code.execute */;
buff.writeInt32BE(len, 1);
buff.write(portal, 5, 'utf-8');
buff[portalLength + 5] = 0; // null terminate portal cString
buff.writeUInt32BE(rows, buff.length - 4);
return buff;
};
const cancel = (processID, secretKey) => {
const buffer = Buffer.allocUnsafe(16);
buffer.writeInt32BE(16, 0);
buffer.writeInt16BE(1234, 4);
buffer.writeInt16BE(5678, 6);
buffer.writeInt32BE(processID, 8);
buffer.writeInt32BE(secretKey, 12);
return buffer;
};
const cstringMessage = (code, string) => {
const stringLen = Buffer.byteLength(string);
const len = 4 + stringLen + 1;
// one extra bit for code
const buffer = Buffer.allocUnsafe(1 + len);
buffer[0] = code;
buffer.writeInt32BE(len, 1);
buffer.write(string, 5, 'utf-8');
buffer[len] = 0; // null terminate cString
return buffer;
};
const emptyDescribePortal = writer.addCString('P').flush(68 /* code.describe */);
const emptyDescribeStatement = writer.addCString('S').flush(68 /* code.describe */);
const describe = (msg) => {
return msg.name
? cstringMessage(68 /* code.describe */, `${msg.type}${msg.name || ''}`)
: msg.type === 'P'
? emptyDescribePortal
: emptyDescribeStatement;
};
const close = (msg) => {
const text = `${msg.type}${msg.name || ''}`;
return cstringMessage(67 /* code.close */, text);
};
const copyData = (chunk) => {
return writer.add(chunk).flush(100 /* code.copyFromChunk */);
};
const copyFail = (message) => {
return cstringMessage(102 /* code.copyFail */, message);
};
const codeOnlyBuffer = (code) => Buffer.from([code, 0x00, 0x00, 0x00, 0x04]);
const flushBuffer = codeOnlyBuffer(72 /* code.flush */);
const syncBuffer = codeOnlyBuffer(83 /* code.sync */);
const endBuffer = codeOnlyBuffer(88 /* code.end */);
const copyDoneBuffer = codeOnlyBuffer(99 /* code.copyDone */);
const serialize = {
startup,
password,
requestSsl,
sendSASLInitialResponseMessage,
sendSCRAMClientFinalMessage,
query,
parse,
bind,
execute,
describe,
close,
flush: () => flushBuffer,
sync: () => syncBuffer,
end: () => endBuffer,
copyData,
copyDone: () => copyDoneBuffer,
copyFail,
cancel,
};
exports.serialize = serialize;
//# sourceMappingURL=serializer.js.map

File diff suppressed because one or more lines are too long