openhim-core
Version:
The OpenHIM core application that provides logging and routing of http requests
186 lines (142 loc) • 7.44 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.fetchRewriteConfig = fetchRewriteConfig;
exports.koaMiddleware = koaMiddleware;
var _url = _interopRequireDefault(require("url"));
var _winston = _interopRequireDefault(require("winston"));
var utils = _interopRequireWildcard(require("../utils"));
var router = _interopRequireWildcard(require("../middleware/router"));
var _config = require("../config");
var _util = require("util");
function _getRequireWildcardCache() { if (typeof WeakMap !== "function") return null; var cache = new WeakMap(); _getRequireWildcardCache = function () { return cache; }; return cache; }
function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
const routerConf = _config.config.get('router'); // see https://regex101.com/r/lW0cN0/1 for an explanation of this regex
const invertPathTransform = pathTransform => pathTransform.replace(/s\/(.*?)\/(.*?)(?:$|\/(.*)$)/, 's/$2/$1/$3');
function fetchRewriteConfig(channel, authType, callback) {
// set the user defined rewrite config from current channel
let rwConfig = [];
if (channel.rewriteUrlsConfig != null) {
rwConfig = rwConfig.concat(channel.rewriteUrlsConfig);
}
if (channel.addAutoRewriteRules) {
/*
* Add in default (virtual) rewrite rules for hosts we proxy
*
* For example if the SHR for some reason sent back a link to a patient in the CR
* (using a direct link to the CR), then if we have a channel that points to the
* CR on a primary route we are able to rewrite the link to point to us instead
* because we know that host.
*/
return utils.getAllChannelsInPriorityOrder((err, channels) => {
if (err != null) {
return callback(err);
}
for (channel of Array.from(channels)) {
for (const route of Array.from(channel.routes)) {
if (route.primary) {
/*
* if this channel has a pathTranform on its primary route then
* invert the path transform so that links that point to this route
* have the path transform reversed when they are rewritten
*
* For example, say we have a channel with urlPattern=/CSD/ and a
* pathTransform on the primary route as follows pathTransform=s/CSD/ihris/
* (ie. the actual server we are proxying is running on http://<host>:<port>/ihirs/).
* If we get links back from this server it will be something like
* http://<host>:<port>/ihirs/something/123 but we need it to be
* http://<him_host>:<him_port>/CSD/something/123. To do this we can reverse
* the pathTransform on the route (s/ihris/CSD/) and apply it while doing the
* rewrite.
*/
let inverseTransform;
let toPort;
if (route.pathTransform) {
inverseTransform = invertPathTransform(route.pathTransform);
} // rewrite to the secure port if tls was used for this transaction
if (authType != null === 'tls') {
toPort = routerConf.httpsPort;
} else {
toPort = routerConf.httpPort;
} // add 'virtual' rewrite config after any user defined config that has been set
rwConfig.push({
fromHost: route.host,
toHost: routerConf.externalHostname,
fromPort: route.port,
toPort,
pathTransform: inverseTransform || null
});
}
}
}
return callback(null, rwConfig);
});
} else {
return callback(null, rwConfig);
}
}
const rewriteUrls = (body, channel, authType, callback) => fetchRewriteConfig(channel, authType, (err, rwConfig) => {
if (err != null) {
return callback(err);
} // rewrite each found href, src or fullUrl attribute (in JSON or XML)
// See https://regex101.com/r/uY3fO1/1 for an explanation of this regex
const newBody = body.replace(/["|']?(?:href|src|fullUrl)["|']?[:|=]\s?["|'](\S*?)["|']/g, (match, hrefUrl) => {
let relativePath;
const hrefUrlObj = _url.default.parse(hrefUrl); // default to using this channel's host if no host so we can match a rewrite rule
if (hrefUrlObj.host == null) {
for (const route of Array.from(channel.routes)) {
if (route.primary) {
hrefUrlObj.hostname = route.host;
hrefUrlObj.port = route.port.toString();
relativePath = true;
break;
}
}
}
for (const rewriteRule of Array.from(rwConfig)) {
// if we find a matching rewrite rule
if (rewriteRule.fromHost.toLowerCase() === hrefUrlObj.hostname && (rewriteRule.fromPort.toString() === hrefUrlObj.port || rewriteRule.fromPort === 80 && hrefUrlObj.port === null)) {
hrefUrlObj.host = null; // so that hostname and port are used separately
hrefUrlObj.hostname = rewriteRule.toHost;
hrefUrlObj.port = rewriteRule.toPort; // rewrite protocol depending on the port the rewriteRule uses
if (hrefUrlObj.protocol) {
if (rewriteRule.toPort === routerConf.httpsPort) {
hrefUrlObj.protocol = 'https';
} else {
hrefUrlObj.protocol = 'http';
}
} // if this rewrite rule requires the path to be transformed then do the transform
if (rewriteRule.pathTransform) {
hrefUrlObj.pathname = router.transformPath(hrefUrlObj.pathname, rewriteRule.pathTransform);
} // we only run the first matching rule found
break;
}
}
if (relativePath) {
// remove the host stuff before formating
hrefUrlObj.host = null;
hrefUrlObj.hostname = null;
hrefUrlObj.port = null;
} // replace the url in the match
const replacement = _url.default.format(hrefUrlObj);
_winston.default.debug(`Rewriting url ${hrefUrl} as ${replacement}`);
return match.replace(hrefUrl, replacement);
});
return callback(null, newBody);
});
if (process.env.NODE_ENV === 'test') {
exports.invertPathTransform = invertPathTransform;
exports.rewriteUrls = rewriteUrls;
}
async function koaMiddleware(ctx, next) {
// do nothing to the request
await next(); // on response rewrite urls
if (ctx.authorisedChannel.rewriteUrls) {
const rewrite = (0, _util.promisify)(rewriteUrls);
ctx.response.body = await rewrite(ctx.response.body.toString(), ctx.authorisedChannel, ctx.authenticationType);
return _winston.default.info(`Rewrote url in the response of transaction: ${ctx.transactionId}`);
}
}
//# sourceMappingURL=rewriteUrls.js.map