openhim-core
Version:
The OpenHIM core application that provides logging and routing of http requests
329 lines (302 loc) • 10.6 kB
JavaScript
var Channel, Q, TaskModel, TransactionModel, activeTasks, config, finalizeTaskRound, findAndProcessAQueuedTask, http, live, logger, net, processNextTaskRound, rerunGetTransaction, rerunHttpRequestSend, rerunMiddleware, rerunSetHTTPRequestOptions, rerunTaskProcessor, rerunTcpRequestSend, rerunTransaction;
TaskModel = require('./model/tasks').Task;
Channel = require('./model/channels').Channel;
Q = require("q");
logger = require("winston");
config = require("./config/config");
config.rerun = config.get('rerun');
http = require('http');
TransactionModel = require("./model/transactions").Transaction;
net = require("net");
rerunMiddleware = require("./middleware/rerunUpdateTransactionTask");
live = false;
activeTasks = 0;
findAndProcessAQueuedTask = function() {
return TaskModel.findOneAndUpdate({
status: 'Queued'
}, {
status: 'Processing'
}, {
'new': true
}, function(err, task) {
if (err) {
return logger.error("An error occurred while looking for rerun tasks: " + err);
} else if (task) {
activeTasks++;
processNextTaskRound(task, function(err) {
if (err) {
logger.error("An error occurred while processing rerun task " + task._id + ": " + err);
}
activeTasks--;
if (live) {
return findAndProcessAQueuedTask();
}
});
if (live) {
return findAndProcessAQueuedTask();
}
}
});
};
rerunTaskProcessor = function() {
if (live) {
findAndProcessAQueuedTask();
return setTimeout(rerunTaskProcessor, config.rerun.processor.pollPeriodMillis);
}
};
exports.start = function(callback) {
live = true;
setTimeout(rerunTaskProcessor, config.rerun.processor.pollPeriodMillis);
logger.info("Started rerun task processor");
return callback();
};
exports.stop = function(callback) {
var waitForActiveTasks;
live = false;
waitForActiveTasks = function() {
if (activeTasks > 0) {
return setTimeout(waitForActiveTasks, 100);
} else {
logger.info("Stopped rerun task processor");
return callback();
}
};
return waitForActiveTasks();
};
exports.isRunning = function() {
return live;
};
finalizeTaskRound = function(task, callback) {
return TaskModel.findOne({
_id: task._id
}, {
status: 1
}, function(err, result) {
if (err) {
return callback(err);
}
if (result.status === 'Processing' && task.remainingTransactions !== 0) {
task.status = 'Queued';
logger.info("Round completed for rerun task #" + task._id + " - " + task.remainingTransactions + " transactions remaining");
} else {
if (task.remainingTransactions === 0) {
task.status = 'Completed';
task.completedDate = new Date();
logger.info("Round completed for rerun task #" + task._id + " - Task completed");
} else {
task.status = result.status;
logger.info("Round completed for rerun task #" + task._id + " - Task has been " + result.status);
}
}
return task.save(function(err) {
return callback(err);
});
});
};
processNextTaskRound = function(task, callback) {
var fn, i, len, nextI, promises, ref, transaction;
logger.debug("Processing next task round: total transactions = " + task.totalTransactions + ", remainingTransactions = " + task.remainingTransactions);
promises = [];
nextI = task.transactions.length - task.remainingTransactions;
ref = task.transactions.slice(nextI, nextI + task.batchSize);
fn = function(transaction) {
var defer;
defer = Q.defer();
rerunTransaction(transaction.tid, task._id, function(err, response) {
if (err) {
transaction.tstatus = 'Failed';
transaction.error = err;
logger.error("An error occurred while rerunning transaction " + transaction.tid + " for task " + task._id + ": " + err);
} else if ((response != null ? response.status : void 0) === 'Failed') {
transaction.tstatus = 'Failed';
transaction.error = response.message;
logger.error("An error occurred while rerunning transaction " + transaction.tid + " for task " + task._id + ": " + err);
} else {
transaction.tstatus = 'Completed';
}
task.remainingTransactions--;
return defer.resolve();
});
transaction.tstatus = 'Processing';
return promises.push(defer.promise);
};
for (i = 0, len = ref.length; i < len; i++) {
transaction = ref[i];
fn(transaction);
}
return (Q.all(promises)).then(function() {
return task.save(function(err) {
if (err != null) {
logger.error("Failed to save current task while processing round: taskID=" + task._id + ", err=" + err, err);
}
return finalizeTaskRound(task, callback);
});
});
};
rerunTransaction = function(transactionID, taskID, callback) {
return rerunGetTransaction(transactionID, function(err, transaction) {
if (err) {
return callback(err);
}
return Channel.findById(transaction.channelID, function(err, channel) {
if (err) {
return callback(err);
}
logger.info("Rerunning " + channel.type + " transaction");
if (channel.type === 'http' || channel.type === 'polling') {
rerunSetHTTPRequestOptions(transaction, taskID, function(err, options) {
if (err) {
return callback(err);
}
return rerunHttpRequestSend(options, transaction, function(err, HTTPResponse) {
return callback(err, HTTPResponse);
});
});
}
if (channel.type === 'tcp' || channel.type === 'tls') {
return rerunTcpRequestSend(channel, transaction, function(err, TCPResponse) {
var ctx;
if (err) {
return callback(err);
}
ctx = {
parentID: transaction._id,
transactionId: transactionID,
transactionStatus: TCPResponse.status,
taskID: taskID
};
return rerunMiddleware.updateOriginalTransaction(ctx, function(err) {
if (err) {
return callback(err);
}
return rerunMiddleware.updateTask(ctx, callback);
});
});
}
});
});
};
rerunGetTransaction = function(transactionID, callback) {
return TransactionModel.findById(transactionID, function(err, transaction) {
if (transaction == null) {
return callback(new Error("Transaction " + transactionID + " could not be found"), null);
}
if (!transaction.canRerun) {
err = new Error("Transaction " + transactionID + " cannot be rerun as there isn't enough information about the request");
return callback(err, null);
}
return callback(null, transaction);
});
};
rerunSetHTTPRequestOptions = function(transaction, taskID, callback) {
var err, options;
if (transaction === null) {
err = new Error("An empty Transaction object was supplied. Aborting HTTP options configuration");
return callback(err, null);
}
logger.info('Rerun Transaction #' + transaction._id + ' - HTTP Request options being configured');
options = {
hostname: config.rerun.host,
port: config.rerun.httpPort,
path: transaction.request.path,
method: transaction.request.method,
headers: transaction.request.headers
};
if (transaction.clientID) {
options.headers.clientID = transaction.clientID;
}
options.headers.parentID = transaction._id;
options.headers.taskID = taskID;
if (transaction.request.querystring) {
options.path += "?" + transaction.request.querystring;
}
return callback(null, options);
};
rerunHttpRequestSend = function(options, transaction, callback) {
var err, req, response;
if (options === null) {
err = new Error("An empty 'Options' object was supplied. Aborting HTTP Send Request");
return callback(err, null);
}
if (transaction === null) {
err = new Error("An empty 'Transaction' object was supplied. Aborting HTTP Send Request");
return callback(err, null);
}
response = {
body: '',
transaction: {}
};
logger.info('Rerun Transaction #' + transaction._id + ' - HTTP Request is being sent...');
req = http.request(options, function(res) {
res.on("data", function(chunk) {
return response.body += chunk;
});
return res.on("end", function(err) {
if (err) {
response.transaction.status = "Failed";
} else {
response.transaction.status = "Completed";
}
response.status = res.statusCode;
response.message = res.statusMessage;
response.headers = res.headers;
response.timestamp = new Date;
logger.info('Rerun Transaction #' + transaction._id + ' - HTTP Response has been captured');
return callback(null, response);
});
});
req.on("error", function(err) {
if (err) {
response.transaction.status = "Failed";
}
response.status = 500;
response.message = "Internal Server Error";
response.timestamp = new Date;
return callback(null, response);
});
if (transaction.request.method === "POST" || transaction.request.method === "PUT") {
req.write(transaction.request.body);
}
return req.end();
};
rerunTcpRequestSend = function(channel, transaction, callback) {
var client, response;
response = {
body: '',
transaction: {}
};
client = new net.Socket();
client.connect(channel.tcpPort, channel.tcpHost, function() {
logger.info("Rerun Transaction " + transaction._id + ": TCP connection established");
client.end(transaction.request.body);
});
client.on("data", function(data) {
return response.body += data;
});
client.on("end", function(data) {
response.status = 200;
response.transaction.status = "Completed";
response.message = '';
response.headers = {};
response.timestamp = new Date;
logger.info('Rerun Transaction #' + transaction._id + ' - TCP Response has been captured');
callback(null, response);
});
return client.on("error", function(err) {
if (err) {
response.transaction.status = "Failed";
}
response.status = 500;
response.message = "Internal Server Error";
response.timestamp = new Date;
return callback(err, response);
});
};
if (process.env.NODE_ENV === "test") {
exports.rerunGetTransaction = rerunGetTransaction;
exports.rerunSetHTTPRequestOptions = rerunSetHTTPRequestOptions;
exports.rerunHttpRequestSend = rerunHttpRequestSend;
exports.rerunTcpRequestSend = rerunTcpRequestSend;
exports.findAndProcessAQueuedTask = findAndProcessAQueuedTask;
}
//# sourceMappingURL=tasks.js.map