pg-server
Version:
Postgres DB server emulator, proxy or honeypot
233 lines • 8.99 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.ResponseWriter = void 0;
const buffer_writer_1 = require("./buffer-writer");
const responses_1 = require("./responses");
const utils_1 = require("../utils");
const util_1 = __importDefault(require("util"));
class ResponseWriter {
constructor(socket) {
this.socket = socket;
this.writer = new buffer_writer_1.Writer();
}
flush(code) {
const buf = this.writer.flush(code);
if (utils_1.isDebug) {
if (code !== responses_1.ResponseCode.ErrorMessage) {
console.log(` ✈ `, (0, responses_1.messageToStr)(code));
}
}
this.socket.write(buf);
}
bindComplete() { this.codeOnly(responses_1.ResponseCode.BindComplete); }
parseComplete() { this.codeOnly(responses_1.ResponseCode.ParseComplete); }
closeComplete() { this.codeOnly(responses_1.ResponseCode.CloseComplete); }
noData() { this.codeOnly(responses_1.ResponseCode.NoData); }
portalSuspended() { this.codeOnly(responses_1.ResponseCode.PortalSuspended); }
copyDone() { this.codeOnly(responses_1.ResponseCode.CopyDone); }
replicationStart() { this.codeOnly(responses_1.ResponseCode.ReplicationStart); }
emptyQuery() { this.codeOnly(responses_1.ResponseCode.EmptyQuery); }
readyForQuery(status) {
var _a;
this.writer.addString((_a = status === null || status === void 0 ? void 0 : status[0]) !== null && _a !== void 0 ? _a : 'I');
return this.flush(responses_1.ResponseCode.ReadyForQuery);
}
codeOnly(code) {
return this.flush(code);
}
dataRow(row) {
this.writer.addInt16(row.length);
for (const r of row) {
if ((0, utils_1.nullish)(r)) {
this.writer.addInt32(-1);
}
else {
const str = stringify(r);
this.writer.addInt32(str.length);
this.writer.addString(str);
}
}
return this.flush(responses_1.ResponseCode.DataRow);
}
command(cmd) {
switch (cmd.type) {
case responses_1.ResponseCode.BackendKeyData:
return this.backendKeyData(cmd.processID, cmd.secretKey);
case responses_1.ResponseCode.BindComplete:
return this.bindComplete();
case responses_1.ResponseCode.CloseComplete:
return this.closeComplete();
case responses_1.ResponseCode.CommandComplete:
return this.commandComplete(cmd.text);
case responses_1.ResponseCode.CopyData:
return this.copyData(cmd.data);
case responses_1.ResponseCode.CopyDone:
return this.copyDone();
case responses_1.ResponseCode.CopyIn:
return this.copyIn(cmd.isBinary, cmd.columnTypes);
case responses_1.ResponseCode.CopyOut:
return this.copyOut(cmd.isBinary, cmd.columnTypes);
case responses_1.ResponseCode.DataRow:
return this.dataRow(cmd.fields);
case responses_1.ResponseCode.EmptyQuery:
return this.emptyQuery();
case responses_1.ResponseCode.ErrorMessage:
return this.error(cmd.message);
case responses_1.ResponseCode.NoData:
return this.noData();
case responses_1.ResponseCode.NoticeMessage:
return this.notice(cmd.message);
case responses_1.ResponseCode.NotificationResponse:
return this.notificationResponse(cmd.processId, cmd.channel, cmd.payload);
case responses_1.ResponseCode.ParameterStatus:
return this.parameterStatus(cmd.name, cmd.value);
case responses_1.ResponseCode.ParseComplete:
return this.parseComplete();
case responses_1.ResponseCode.PortalSuspended:
return this.portalSuspended();
case responses_1.ResponseCode.ReadyForQuery:
return this.readyForQuery(cmd.status);
case responses_1.ResponseCode.ReplicationStart:
return this.replicationStart();
case responses_1.ResponseCode.RowDescriptionMessage:
return this.rowDescription(cmd.fields);
case responses_1.ResponseCode.AuthenticationResponse:
if (cmd.kind === 'ok') {
return this.authenticationOk();
}
throw new Error('Command has no writer: ' + (0, responses_1.messageToStr)(cmd.type));
default:
(0, utils_1.assertNever)(cmd);
}
}
commandComplete(message) {
this.writer.addCString(message);
return this.flush(responses_1.ResponseCode.CommandComplete);
}
notificationResponse(pid, channel, payload) {
this.writer.addInt32(pid);
this.writer.addCString(channel);
this.writer.addCString(payload);
return this.flush(responses_1.ResponseCode.NotificationResponse);
}
parameterStatus(name, value) {
this.writer.addCString(name);
this.writer.addCString(value);
return this.flush(responses_1.ResponseCode.ParameterStatus);
}
backendKeyData(pid, secretKey) {
this.writer.addInt32(pid);
this.writer.addInt32(secretKey);
return this.flush(responses_1.ResponseCode.BackendKeyData);
}
error(error) {
this.errorMessage(error, responses_1.ResponseCode.ErrorMessage);
}
notice(error) {
this.errorMessage(error, responses_1.ResponseCode.NoticeMessage);
}
errorMessage(error, code) {
error = error instanceof Error
? util_1.default.inspect(error)
: error;
error = typeof error === 'string'
? { message: error }
: error;
if (utils_1.isDebug) {
console.warn(` ✈⚠ `, error.message);
}
// https://www.postgresql.org/docs/12/protocol-error-fields.html
for (const [k, v] of Object.entries(error)) {
const mk = noticeMapping[k];
if (mk && typeof v === 'string' && v) {
this.writer.addString(mk[0]);
this.writer.addCString(v);
}
}
this.writer.addString('\0');
return this.flush(code);
}
parameterDescription(types) {
this.writer.addInt16(types.length);
for (const t of types) {
this.writer.addInt32(t);
}
return this.flush(responses_1.ResponseCode.ParameterDescription);
}
rowDescription(fieldDescs) {
this.writer.addInt16(fieldDescs.length);
for (const f of fieldDescs) {
this.writer.addCString(f.name);
this.writer.addInt32(f.tableID);
this.writer.addInt16(f.columnID);
this.writer.addInt32(f.dataTypeID);
this.writer.addInt16(f.dataTypeSize);
this.writer.addInt32(f.dataTypeModifier);
this.writer.addInt16(f.mode === 'text' ? 0 : 1);
}
return this.flush(responses_1.ResponseCode.RowDescriptionMessage);
}
copyIn(isBinary, types) { this.copyMessage(isBinary, types, responses_1.ResponseCode.CopyIn); }
copyOut(isBinary, types) { this.copyMessage(isBinary, types, responses_1.ResponseCode.CopyOut); }
copyMessage(isBinary, types, code) {
this.writer.byte(isBinary ? 1 : 0);
this.writer.addInt16(types.length);
for (const t of types) {
this.writer.addInt16(t);
}
return this.flush(code);
}
copyData(data) {
this.writer.add(data);
return this.flush(responses_1.ResponseCode.CopyData);
}
authenticationOk() {
this.writer.addInt32(0);
return this.flush(responses_1.ResponseCode.AuthenticationResponse);
}
}
exports.ResponseWriter = ResponseWriter;
const noticeMapping = {
'message': 'M',
'severity': 'S',
'code': 'C',
'detail': 'D',
'hint': 'H',
'position': 'P',
'internalPosition': 'p',
'internalQuery': 'q',
'where': 'W',
'schema': 's',
'table': 't',
'column': 'c',
'dataType': 'd',
'constraint': 'n',
'file': 'F',
'line': 'L',
'routine': 'R',
};
function stringify(value) {
if ((0, utils_1.nullish)(value)) {
return '';
}
if (typeof value === 'string') {
return value;
}
if (typeof value === 'number') {
return String(value);
}
if (typeof value === 'boolean') {
return value ? 't' : 'f';
}
if (value instanceof Date) {
return value.toISOString();
}
if (Buffer.isBuffer(value)) {
return value.toString('hex');
}
return JSON.stringify(value);
}
//# sourceMappingURL=response-writer.js.map