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.

213 lines
8.6 KiB

6 years ago
/* jshint -W097 */// jshint strict:false
/*jslint node: true */
/*jshint -W061 */
'use strict';
/**
* Proxy class
*
* From settings used only secure, auth and crossDomain
*
* @class
* @param {object} server http or https node.js object
* @param {object} webSettings settings of the web server, like <pre><code>{secure: settings.secure, port: settings.port}</code></pre>
* @param {object} adapter web adapter object
* @param {object} instanceSettings instance object with common and native
* @param {object} app express application
* @return {object} object instance
*/
function Proxy(server, webSettings, adapter, instanceSettings, app) {
if (!(this instanceof Proxy)) return new Proxy(server, webSettings, adapter, instanceSettings, app);
this.app = app;
this.adapter = adapter;
this.settings = webSettings;
this.config = instanceSettings ? instanceSettings.native : {};
this.namespace = instanceSettings ? instanceSettings._id.substring('system.adapter.'.length) : 'simple-api';
this.request = {};
var that = this;
var proxy;
var path;
var fs;
this.config.errorTimeout = parseInt(this.config.errorTimeout, 10) || 10000;
if (this.config.errorTimeout < 1000) this.config.errorTimeout = 1000;
this.config.route = this.config.route || (that.namespace + '/');
var mime = require('mime');
this.interval = setInterval(function () {
var now = Date.now();
for (var e = 0; e < that.config.rules.length; e++) {
var rule = that.config.rules[e];
if (!rule.request) continue;
for (var u = rule.request.length - 1; u >= 0; u--) {
if (rule.request[u].res.finished) {
rule.request.splice(u, 1);
} else
if (now - rule.request[u].ts > rule.timeout) {
rule.lastError = new Date().getTime();
rule.lastErrorText = 'timeout';
adapter.log.error('[proxy] Cannot get "' + rule.request[u].req.url + '": timeout');
rule.request.splice(u, 1);
}
}
}
}, 10000);
function finishReq(rule, req, res) {
if (rule.request) {
var entry = rule.request.find(function (e) {
return e.res === res;
});
if (!entry) {
adapter.log.error('Request "' + req.url + '" not found in requests');
} else {
var ppos = rule.request.indexOf(entry);
rule.request.splice(ppos, 1);
}
} else {
adapter.log.error('URL "' + url + '" not found in requests');
}
}
function oneRule(rule) {
adapter.log.info('Install extension on /' + that.config.route + rule.regex);
rule.timeout = parseInt(rule.timeout, 10) || that.config.errorTimeout;
if (rule.url.match(/^https?:\/\//)) {
proxy = proxy || require('http-proxy-middleware');
var options = {
target: rule.url,
ws: true,
secure: false,
changeOrigin: false,
proxyTimeout: rule.timeout,
xfwd: true,
onError: function (err, req, res/* , url*/) {
rule.lastError = new Date().getTime();
rule.lastErrorText = err.toString();
adapter.log.error('[proxy] Cannot get "' + rule.url + '": ' + err);
if (!res.finished) {
try {
if (typeof res.status === 'function') {
res.status(500).send(err);
} else if (typeof res.send === 'function') {
res.send(err);
} else {
adapter.log.error('[proxy] Cannot response');
}
} catch (e) {
adapter.log.error('[proxy] Cannot response: ' + e);
}
}
finishReq(rule, req, res);
},
onProxyReq: function (req /* , origReq, res, options */) {
adapter.log.debug(req.method + ': ' + rule.url + req.path);
},
onProxyRes: function (req, reqOrig, res) {
finishReq(rule, reqOrig, res);
rule.lastError = 0;
rule.lastErrorText = '';
adapter.log.debug('[proxy] Response for ' + reqOrig.url + ': ' + req.statusCode + '(' + req.statusMessage + ')');
},
/*onProxyReqWs: function () {
console.log('onProxyReqWs');
},
onOpen: function () {
console.log('onOpen');
},
onClose: function () {
console.log('onClose');
},*/
pathRewrite: {}
};
var m = rule.url.match(/^https?:\/\/(.+)@/);
if (m && m[1] && m[1].indexOf(':') !== -1) {
rule.url = rule.url.replace(m[1] + '@', '');
options.auth = decodeURIComponent(m[1]);
}
options.pathRewrite['^/' + that.config.route + rule.regex] = '/';
rule.handler = proxy(options);
that.app.use('/' + that.config.route + rule.regex, function (req, res, next) {
var now = Date.now();
if (rule.lastError && ((now - rule.lastError) < that.config.errorTimeout)) {
res.status(404).send('[proxy] Cannot read file: ' + rule.lastErrorText);
return
}
if (rule.request && rule.request.length > 10) {
res.status(500).send('[proxy] too many parallel requests for "' + req.url + '"!');
adapter.log.warn('[proxy] too many parallel requests for "' + req.url + '"');
return;
}
rule.request = rule.request || [];
rule.request.push({req: req, res: res, ts: now});
rule.handler(req, res, next);
}.bind(this));
} else {
path = path || require('path');
fs = fs || require('fs');
rule.url = rule.url.replace(/\\/g, '/');
if (rule.url[0] !== '/' && !rule.url.match(/^[A-Za-z]:/)) {
rule.url = path.normalize(__dirname + '/../../' + rule.url);
}
// file handler
that.app.use('/' + that.config.route + rule.regex, function (req, res, next) {
var fileName = rule.url + req.url;
if (fs.existsSync(fileName)) {
var stat = fs.statSync(fileName);
if (stat.isDirectory()) {
var dirs = fs.readdirSync(fileName);
var text = '';
dirs.sort();
for (var d = 0; d < dirs.length; d++) {
text += (text ? '<br>' : '') + '<a href="./' + dirs[d] + '">' + dirs[d] + '</a>';
}
res.set('Content-Type', 'text/html');
res.status(200).send('<html><head><title>' + fileName + '</title></head><body>' + text + '</body>');
} else {
var data;
try {
data = fs.readFileSync(fileName);
} catch (e) {
res.status(500).send('[proxy] Cannot read file: ' + e);
return;
}
res.contentType(mime.lookup(path.extname(fileName).substring(1)));
res.status(200).send(data);
}
} else {
res.status(404).send('[proxy] File "' + fileName +'" not found.');
}
});
}
}
this.destroy = function () {
if (this.interval) {
clearInterval(this.interval);
this.interval = null;
}
};
var __construct = (function () {
for (var e = 0; e < this.config.rules.length; e++) {
oneRule(this.config.rules[e]);
}
}.bind(this))();
}
module.exports = Proxy;