DTP Base provides a scalable and secure Node.js application development harness ready for production service.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

145 lines
3.9 KiB

// host-cache.js
// Copyright (C) 2024 DTP Technologies, LLC
// All Rights Reserved
'use strict';
import dgram from 'node:dgram';
import { v4 as uuidv4 } from 'uuid';
import { SiteService, SiteError } from '../../lib/site-lib.js';
export default class HostCacheService extends SiteService {
static get name ( ) { return 'HostCacheService'; }
static get slug ( ) { return 'hostCache'; }
constructor (dtp) {
super(dtp, HostCacheService);
this.transactions = { };
}
async start ( ) {
await super.start();
this.log.info('creating UDP host-cache socket');
this.hostCache = dgram.createSocket('udp4', this.onMessage.bind(this));
this.hostCache.on('error', this.onError.bind(this));
this.log.info('connecting UDP host-cache socket');
// this.hostCache.bind(0, '127.0.0.1');
this.hostCache.connect(
parseInt(process.env.HOST_CACHE_PORT || '8000', 10),
process.env.HOST_CACHE_HOST || '127.0.0.1',
);
}
async stop ( ) {
if (this.hostCache) {
this.log.info('disconnecting UDP host-cache socket');
this.hostCache.disconnect();
delete this.hostCache;
}
}
async getFile (bucket, key) {
return new Promise((resolve, reject) => {
const transaction = { tid: uuidv4(), bucket, key, resolve, reject };
this.transactions[transaction.tid] = transaction;
const message = JSON.stringify({
tid: transaction.tid,
cmd: 'getFile',
params: { bucket, key },
});
this.hostCache.send(message);
});
}
async fetchUrl (url) {
return new Promise((resolve, reject) => {
const transaction = { tid: uuidv4(), url, resolve, reject };
this.transactions[transaction.tid] = transaction;
const message = JSON.stringify({
tid: transaction.tid,
cmd: 'fetchUrl',
params: { url },
});
this.hostCache.send(message);
});
}
async onMessage (message, rinfo) {
message = message.toString('utf8');
message = JSON.parse(message);
switch (message.res.cmd) {
case 'getFile':
return this.onGetFile(message, rinfo);
case 'fetchUrl':
return this.onFetchUrl(message, rinfo);
}
}
async onGetFile (message) {
const transaction = this.transactions[message.tid];
if (!transaction) {
this.log.error('getFile response received with no matching transaction', { tid: message.tid });
return;
}
delete this.transactions[message.tid];
if (!message.res.success) {
transaction.reject(new SiteError(message.res.statusCode, message.res.message));
return;
}
transaction.resolve({
success: message.res.success,
message: message.res.message,
file: message.res.file,
flags: message.flags,
duration: message.duration,
});
}
async onFetchUrl (message) {
const transaction = this.transactions[message.tid];
if (!transaction) {
this.log.error('fetchUrl response received with no matching transaction', { tid: message.tid });
return;
}
delete this.transactions[message.tid];
if (!message.res.success) {
transaction.reject(new SiteError(message.res.statusCode || 500, message.res.message));
return;
}
transaction.resolve({
success: message.res.success,
message: message.res.message,
file: message.res.file,
flags: message.flags,
duration: message.duration,
});
}
async onError (error) {
this.log.error('onError', { error });
if ((error.errno !== -111) || (error.code !== 'ECONNREFUSED')) {
return;
}
if (!this.transactions) {
return;
}
if (this.transactions) {
for (const key in this.transactions) {
this.log.alert('destroying host cache transaction', { key });
const transaction = this.transactions[key];
transaction.reject(error);
delete this.transactions[key];
}
}
}
}