nocca
Version:
NodeJS Mock Server
187 lines (116 loc) • 5.16 kB
JavaScript
var $utils = require('./utils');
var $q = require('q');
var $url = require('url');
var $extend = require('extend');
module.exports = Forwarder;
function Forwarder (Nocca) {
var self = this;
self.logger = Nocca.logger.child({ module: 'Forwarder' });
var PARAM_REGEX = /:[a-zA-Z_-]+/g;
forwardRequest.prepareProxyRequest = prepareProxyRequest;
return forwardRequest;
function forwardRequest (reqContext) {
var deferred = $q.defer();
var endpointDef = reqContext.endpoint.definition;
// generally this should be caught by the endpoint selector..
// but since everything is configurable, swapping that with a lesser alternative might bring us here
if (!endpointDef) {
self.logger.warn('Not forwarding, missing endpoint definition');
reqContext.errors.throwNoEndpointError();
}
else if (reqContext.getPlaybackResponse() &&
reqContext.config.forward !== Nocca.constants.FORWARDING_FORWARD_ALL) {
self.logger.debug('Not forwarding, cache response was found.');
deferred.resolve(reqContext);
}
else {
if (reqContext.getPlaybackResponse() &&
reqContext.config.forward === Nocca.constants.FORWARDING_FORWARD_ALL) {
self.logger.debug('Forwarding is forced by config');
}
var proxyReq = reqContext.getProxyRequest();
self.logger.info('>>>> Forwarding request to: ' + proxyReq.host);
_performRequestForward(reqContext, deferred);
}
return deferred.promise;
}
function prepareProxyRequest (reqContext) {
var deferred = $q.defer();
var endpointDef = reqContext.endpoint.definition;
if (endpointDef) {
var req = reqContext.getClientRequest();
// extract details from the original request
var proxyReq = Nocca.httpMessageFactory.createRequest();
proxyReq.method = req.method;
proxyReq.headers = $extend({}, req.headers);
proxyReq.setBody(req.getBody());
var resolvedUrl = endpointDef.targetBaseUrl;
// parse any params in the url
var urlParams = resolvedUrl.match(PARAM_REGEX);
if (urlParams) {
urlParams.forEach(function (param) {
resolvedUrl = resolvedUrl.replace(param, reqContext.endpoint.params[param.substr(1)]);
});
}
// create a target URL based on remainingUrl content
var parsedRemainingUrl = $url.parse(reqContext.endpoint.remainingUrl);
if (parsedRemainingUrl.pathname !== null) {
// some path remained, glue with '/' if required
resolvedUrl += '/' + reqContext.endpoint.remainingUrl.replace(/^\//, '');
}
else if (parsedRemainingUrl.search !== null) {
// join without glue
resolvedUrl += reqContext.endpoint.remainingUrl;
}
var targetUrl = $url.parse(resolvedUrl);
proxyReq.protocol = targetUrl.protocol;
proxyReq.host = targetUrl.hostname;
proxyReq.port = targetUrl.port;
proxyReq.path = targetUrl.path;
// sync up host header with new target
proxyReq.headers.host = proxyReq.host;
// format headers
proxyReq.headers = $utils.camelCaseAndDashHeaders(proxyReq.headers, [
/* remove these headers */
], [
/* dont parse these headers */
]);
// save in reqContext
reqContext.setProxyRequest(proxyReq);
}
deferred.resolve(reqContext);
return deferred.promise;
}
function _performRequestForward (reqContext, deferred) {
var proxyReq = reqContext.getProxyRequest();
var request = proxyReq.sendAsRequest();
request.on('response', function (response) {
var proxyRes = Nocca.httpMessageFactory.createResponse();
proxyRes.readIncomingMessage(response)
.then(function () {
return proxyRes.unpack();
})
.then(function () {
reqContext.setProxyResponse(proxyRes);
self.logger.info('<<<< Response from endpoint');
deferred.resolve(reqContext);
}).fail(function () {
self.logger.error('Forwarding failed');
try {
reqContext.errors.throwForwardingFailedError();
} catch (e) {
deferred.reject(e);
}
});
});
request.on('error', function (err) {
self.logger.error('Error connecting to remote service: ' + err.message);
try {
reqContext.errors.throwForwardingFailedError();
} catch (e) {
deferred.reject(e);
}
});
}
}
;