UNPKG

openhim-core

Version:

The OpenHIM core application that provides logging and routing of http requests

137 lines (116 loc) 5.72 kB
url = require 'url' winston = require 'winston' Q = require 'q' Channel = (require '../../lib/model/channels').Channel config = require '../config/config' routerConf = config.get 'router' utils = require '../../lib/utils' router = require '../../lib/middleware/router' invertPathTransform = (pathTransform) -> # see https://regex101.com/r/lW0cN0/1 for an explanation of this regex return pathTransform.replace /s\/(.*?)\/(.*?)(?:$|\/(.*)$)/, 's/$2/$1/$3' exports.fetchRewriteConfig = (channel, authType, callback) -> # set the user defined rewrite config from current channel rwConfig = [] if channel.rewriteUrlsConfig? 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. ### utils.getAllChannelsInPriorityOrder (err, channels) -> if err? return callback err for channel in channels for route in channel.routes do -> 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. ### if route.pathTransform inverseTransform = invertPathTransform route.pathTransform # rewrite to the secure port if tls was used for this transaction if authType? is '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': toPort 'pathTransform': if inverseTransform then inverseTransform else null callback null, rwConfig else callback null, rwConfig rewriteUrls = (body, channel, authType, callback) -> exports.fetchRewriteConfig channel, authType, (err, rwConfig) -> if err? 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 newBody = body.replace /["|']?(?:href|src|fullUrl)["|']?[:|=]\s?["|'](\S*?)["|']/g, (match, hrefUrl) -> hrefUrlObj = url.parse hrefUrl # default to using this channel's host if no host so we can match a rewrite rule if not hrefUrlObj.host? for route in channel.routes if route.primary hrefUrlObj.hostname = route.host hrefUrlObj.port = route.port.toString() relativePath = true break for rewriteRule in rwConfig # if we find a matching rewrite rule if rewriteRule.fromHost.toLowerCase() is hrefUrlObj.hostname and (rewriteRule.fromPort.toString() is hrefUrlObj.port or (rewriteRule.fromPort is 80 and hrefUrlObj.port is 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 is 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 replacement = url.format hrefUrlObj winston.debug "Rewriting url #{hrefUrl} as #{replacement}" return match.replace hrefUrl, replacement callback null, newBody if process.env.NODE_ENV is 'test' exports.invertPathTransform = invertPathTransform exports.rewriteUrls = rewriteUrls exports.koaMiddleware = (next) -> # do nothing to the request yield next # on response rewrite urls if this.authorisedChannel.rewriteUrls rewrite = Q.denodeify rewriteUrls this.response.body = yield rewrite this.response.body.toString(), this.authorisedChannel, this.authenticationType winston.info "Rewrote url in the response of transaction: #{this.transactionId}"