@moquyun/proxy
Version:
Multi-user & multi-env web debugging proxy based on whistle
123 lines (113 loc) • 3.09 kB
JavaScript
const WORKER_NUM = require('../workerNum');
const config = require('../../config');
const STATUS = { workerNum: WORKER_NUM };
let allWorkers = [];
let envWorkerMap = {};
let curWorkerInfo;
const MAX_CACHE_TIME = 1000 * 60 * 8;
const WORKER_INFO_RE = /^\d{1,3}(\.\d{1,3}){3}:\d{1,5}\/[1-9]\d*(,\d{1,3}(\.\d{1,3}){3}:\d{1,5}\/[1-9]\d*)*$/;
const parseWorker = (item) => {
item = item.split('/');
const index = parseInt(item[1], 10);
item = item[0].split(':');
return {
index,
envNum: 0,
host: item[0],
port: parseInt(item[1], 10),
};
};
setInterval(() => {
const now = Date.now();
let isChanged;
Object.keys(envWorkerMap).forEach((env) => {
const { updateTime, worker } = envWorkerMap[env];
if (now - updateTime > MAX_CACHE_TIME) {
delete envWorkerMap[env];
--worker.envNum;
isChanged = true;
}
});
if (isChanged) {
allWorkers.sort((w1, w2) => {
return w1.envNum > w2.envNum ? 1 : -1;
});
}
}, 30000);
const flattenWorkers = (workerList) => {
const result = [];
let len = workerList.length;
while (workerList.length) {
for (let i = len - 1; i >= 0; i--) {
const worker = workerList[i];
--worker.index;
if (!worker.index) {
workerList.splice(i, 1);
}
result.push(Object.assign({}, worker, STATUS));
}
len = workerList.length;
}
return result;
};
const update = (options) => {
if (!options || curWorkerInfo === options) {
return allWorkers;
}
let workerList;
try {
workerList = Buffer.from(options, 'base64').toString();
if (!WORKER_INFO_RE.test(workerList)) {
return allWorkers;
}
} catch (e) {
return allWorkers;
}
curWorkerInfo = options;
workerList = workerList.split(',').map(parseWorker);
allWorkers = flattenWorkers(workerList);
envWorkerMap = {};
return allWorkers;
};
const notEmptyStr = (str) => {
return str && typeof str === 'string';
};
const getWorker = ({ space, group, env }, servers) => {
if (!notEmptyStr(space) || !notEmptyStr(group) || !update(servers).length) {
return STATUS;
}
env = [space, group, env || ''].join('/');
const info = envWorkerMap[env];
if (info) {
info.updateTime = Date.now();
return info.worker;
}
const worker = allWorkers[0];
++worker.envNum;
envWorkerMap[env] = {
updateTime: Date.now(),
worker,
};
const index = allWorkers.indexOf(worker);
for (let i = allWorkers.length - 1; i > index; i--) {
const next = allWorkers[i];
if (!next || worker.envNum > next.envNum) {
allWorkers[i] = worker;
allWorkers[index] = next;
return worker;
}
}
return worker;
};
module.exports = (ctx) => {
const servers = ctx.get('x-nohost-servers');
const { query } = ctx.request;
const status = Object.assign({}, getWorker(query, servers));
status.totalReqs = config.totalReqs;
status.uiReqs = config.uiReqs;
status.upgradeReqs = config.upgradeReqs;
status.tunnelReqs = config.tunnelReqs;
status.totalQps = config.totalQps;
status.uiQps = config.uiQps;
ctx.body = status;
};