loganalysis
Version:
Collects stdout and stderr messages from Node.js applications and sends them to the IBM SmartCloud Analytics - Log Analysis service.
436 lines (409 loc) • 15.2 kB
JavaScript
/*
* © Copyright IBM Corp. 2014
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
var fs = require('fs');
var assert = require('assert');
var url = require('url');
var querystring = require('querystring');
var logger = require('./logger').newLogger('restClient');
const USER_AGENT = 'Rest Client';
const REAUTH_BEFORE = 2; //minutes (must be less than the value of cacheCushionMax, which defaults to 3 mins)
function RestClient(options) {
logger.debug('Entering RestClient constructor');
var urlOptions = url.parse(options.url);
this.requestPath = urlOptions.path == '/' ? '' : urlOptions.path;
this.hostname = urlOptions.hostname;
this.httpProtocol = urlOptions.protocol == 'http:';
this.port = urlOptions.port;
this.client = this.httpProtocol ? require('http') : require('https');
this.username = options.username;
this.password = options.password;
this.app_guid = options.app_guid;
this.isCloud = options.isCloud;
this.CSRFToken = null;
this.LTPAToken = null;
this.reinitInProgress = false;
this.login_data = querystring.stringify({ 'action' : 'Go', 'j_password' : this.password, 'j_username' : this.username });
this.login_opts = {
host : this.hostname,
path : '/Unity/j_security_check',
method : 'POST',
headers : {
'Content-Type' : 'application/x-www-form-urlencoded',
'Content-Length' : this.login_data.length,
'accept' : '*/*',
Host: this.hostname
}
};
if(!this.isCloud) {
this.ca = fs.readFileSync('client.crt');
this.agent = new this.client.Agent({ host: 'localhost', port: 1337, ca: this.ca, rejectUnauthorized: false });
this.agent.maxSockets = 10; this.login_opts.port = this.port;
this.login_opts.agent = this.agent;
this.login_opts.ca = [ this.ca ];
this.login_opts.headers.Host = this.hostname + ':' + this.port;
}
logger.debug('Exiting RestClient constructor');
}
RestClient.prototype.reinit = function() {
this.reinitInProgress = true;
logger.debug('Entering RestClient.prototype.reinit');
this.doLogout();
var _this = this;
setTimeout(function(){
_this.doLogin();
}, 5000);
logger.debug('Exiting RestClient.prototype.reinit');
}
RestClient.prototype.isHTTPProtocol = function () {
return this.httpProtocol;
};
RestClient.prototype.getServerName = function () {
return this.hostname;
};
RestClient.prototype.deleteOnce = function (path, callback, options, eventsCallbacks) {
logger.debug('Entering RestClient.prototype.deleteOnce');
var deleteOptions = this.createRequestOptions(path, 'DELETE', options);
var req = this.client.request(deleteOptions, callback);
if (eventsCallbacks) {
for (var event in eventsCallbacks) {
if (eventsCallbacks.hasOwnProperty(event)) {
req.on(event, eventsCallbacks[event]);
}
}
} else {
req.on('error', onRequestError);
}
req.end();
logger.debug('Exiting RestClient.prototype.deleteOnce');
};
RestClient.prototype.getOnce = function (path, callback, options, eventsCallbacks) {
logger.debug('Entering RestClient.prototype.getOnce');
var getOptions = this.createRequestOptions(path, 'GET', options);
var req = this.client.request(getOptions, callback);
if (eventsCallbacks) {
for (var event in eventsCallbacks) {
if (eventsCallbacks.hasOwnProperty(event)) {
req.on(event, eventsCallbacks[event]);
}
}
} else {
req.on('error', onRequestError);
}
req.end();
logger.debug('Exiting RestClient.prototype.getOnce');
};
RestClient.prototype.get = function (path, callback, options, eventsCallbacks) {
logger.debug('Entering RestClient.prototype.get');
var getOptions = this.createRequestOptions(path, 'GET', options);
var req = this.client.request(getOptions, callback);
if (eventsCallbacks) {
for (var event in eventsCallbacks) {
req.on(event, eventsCallbacks[event]);
}
} else {
req.on('error', onRequestError);
}
logger.debug('Entering RestClient.prototype.get');
return req;
};
RestClient.prototype.postOnce = function (path, body, callback, options, eventsCallbacks) {
logger.debug('Entering RestClient.prototype.postOnce');
var postOptions = this.createRequestOptions(path, 'POST', options);
var req = this.client.request(postOptions, callback);
req.setHeader('Accept', 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8');
req.setHeader('Content-Language', 'en-US');
req.setHeader('Content-Length', Buffer.byteLength(body));
req.write(body);
if (eventsCallbacks) {
for (var event in eventsCallbacks) {
req.on(event, eventsCallbacks[event]);
}
} else {
req.on('error', onRequestError);
}
req.end();
logger.debug('Exiting RestClient.prototype.postOnce');
};
RestClient.prototype.post = function (path, callback, options, eventsCallbacks) {
logger.debug('Entering RestClient.prototype.post');
var postOptions = this.createRequestOptions(path, 'POST', options);
var req = this.client.request(postOptions, callback);
if (eventsCallbacks) {
for (var event in eventsCallbacks) {
req.on(event, eventsCallbacks[event]);
}
} else {
req.on('error', onRequestError);
}
logger.debug('Exiting RestClient.prototype.post');
return req;
};
RestClient.prototype.putOnce = function (path, body, callback, options, eventsCallbacks) {
logger.debug('Entering RestClient.prototype.putOnce');
var putOptions = this.createRequestOptions(path, 'PUT', options);
var req = this.client.request(putOptions, callback);
req.setHeader('content-length', Buffer.byteLength(body));
if (eventsCallbacks) {
for (var event in eventsCallbacks) {
req.on(event, eventsCallbacks[event]);
}
} else {
req.on('error', onRequestError);
}
req.write(body);
req.end();
logger.debug('Exiting RestClient.prototype.putOnce');
};
RestClient.prototype.put = function (path, body, callback, options, eventsCallbacks) {
logger.debug('Entering RestClient.prototype.put');
var putOptions = this.createRequestOptions(path, 'PUT', options);
var req = this.client.request(putOptions, callback);
req.setHeader('Content-Length', Buffer.byteLength(body));
if (eventsCallbacks) {
for (var event in eventsCallbacks) {
req.on(event, eventsCallbacks[event]);
}
} else {
req.on('error', onRequestError);
}
req.write(body);
logger.debug('Exiting RestClient.prototype.put');
return req;
};
RestClient.prototype.createRequestOptions = function (path, method, options) {
logger.debug('Entering RestClient.prototype.createRequestOptions');
var ltpa = this.LTPAToken;
var csrf = this.CSRFToken;
if (ltpa == null || csrf == null) {
logger.error('LTPA or CSRF token is null');
}
var cookie = ltpa && csrf ? ltpa + csrf + ';' : '';
if(csrf && (method == 'PUT' || method == 'POST')){
path = path + (path.indexOf('?') > 0 ? '&' : '?') + csrf;
}
var reqOpts = {
hostname: this.hostname,
port: this.port,
path: this.requestPath + path,
method: method,
headers: {
'User-Agent': USER_AGENT,
'Cookie': [cookie]
}
};
if(!this.isCloud) {
reqOpts.agent = this.agent;
}
if (method == 'POST' || method == 'PUT') {
reqOpts.headers['Content-Type'] = 'application/json';
}
if (options) {
for (var key in options) {
if (key == 'headers') {
var headers = options.headers;
for (var headerName in headers) {
reqOpts.headers[headerName] = headers[headerName];
}
} else {
reqOpts[key] = options[key];
}
}
}
logger.debug('Exiting RestClient.prototype.createRequestOptions');
return reqOpts;
};
function onRequestError(e) {
logger.error('Fail to execute request, the error message is ' + (e && e.message ? e.message : ''));
}
RestClient.prototype.doLogout = function () {
logger.debug('Entering RestClient.prototype.doLogout');
logger.debug('Trying to log out from SCALA');
var getReq = this.get('/Unity/jsp/logoff.jsp?' + this.CSRFToken, function(res) {
var getResponseData = '';
res.on('data', function (data) {
getResponseData += data;
});
res.on('end', function () {
logger.debug('Response data for logout ' + getResponseData);
});
res.on('error', function (e) {
logger.error('Failed to log off due to ' + (e && e.message ? e.message : ''));
});
if (res.statusCode == 302) {
logger.debug('Cookies in logout response header: ' + res.headers['set-cookie']);
this.LTPAToken = null;
this.CSRFToken = null;
logger.info('Logged out of scala');
}
});
getReq.on('error', function (e) {
logger.error('Failed to logout due to ' + (e && e.message ? e.message : ''));
});
getReq.end();
logger.debug('Exiting RestClient.prototype.doLogout');
}
RestClient.prototype.reauthenticate = function () {
logger.debug('Entering RestClient.prototype.reauthenticate');
var _this = this;
try{
logger.debug('Trying to re-authenticate with SCALA, as the LTPA token is about to expire');
var reauthReq = _this.get('/Unity/LTPAToken/reauthenticate?j_username=' + _this.username + '&j_password=' + _this.password, function(res){
var reauthResData = '';
res.on('data', function (d) {
reauthResData += d;
});
res.on('end', function () {
logger.debug('Response for LTPA reauthentication ' + reauthResData);
if (reauthResData.trim() != '') {
var reauthRes = JSON.parse(reauthResData);
if (reauthRes && reauthRes.reauthenticate === true) {
var ltpa = res.headers['set-cookie'][0].split(';')[0].trim() + ';';
if (ltpa === _this.LTPAToken) {
logger.error('New LTPA token is same as earlier one!');
}
logger.debug('Re-authentication successful!');
_this.LTPAToken = ltpa;
_this.scheduleReauth();
} else {
logger.error('Could not re-authenticate into SCALA');
}
} else {
logger.error('Could not re-authenticate into SCALA, received empty response!');
}
});
res.on('error', function(e) {
logger.error('Could not log reauthenticate, due to: ' + e);
});
});
reauthReq.end();
} catch (e) {
logger.error('Clould not re-authenticate, due to ' + e);
}
logger.debug('Exiting RestClient.prototype.reauthenticate');
}
RestClient.prototype.scheduleReauth = function () {
logger.debug('Entering RestClient.prototype.scheduleReauth');
var _this = this;
try{
var expReq = this.get('/Unity/LTPAToken/expiration', function(res){
var expResData = '';
res.on('data', function (data) {
expResData += data;
});
expReq.on('error', function(e) {
logger.error(e);
logger.error('Could not get LTPA exipry period');
});
res.on('end', function () {
assert.strictEqual(200, res.statusCode);
logger.debug('Response for querying LTPA expiration ' + expResData);
if (expResData.trim() != '') {
var ltpaExpInfo = JSON.parse(expResData);
var timeout = ltpaExpInfo.ltpaexpiration - ltpaExpInfo.currentservertime - REAUTH_BEFORE * 60000;
logger.debug('Scheduling reauthentication after ' + timeout/1000 + ' seconds');
setTimeout(function() {
_this.reauthenticate();
}, timeout);
} else {
logger.error('Could not get LTPA exipry period, received empty response!');
}
});
res.on('error', function(e) {
logger.error('Could not schedule reauthentication, due to: ' + e);
});
});
expReq.end();
} catch (e) {
logger.error('Clould not schedule reauthentication, due to: ' + e);
}
logger.debug('Exiting RestClient.prototype.scheduleReauth');
}
RestClient.prototype.doLogin = function () {
logger.debug('Entering RestClient.prototype.doLogin');
logger.debug('Trying to log into SCALA');
var _this = this;
try {
var post_req = this.client.request(this.login_opts, function(res) {
res.setEncoding('utf8');
res.on('error', function (e) {
logger.error('Failed to log in to scala server due to ' + (e && e.message ? e.message : ''));
});
res.on('data', function (data) {});
res.on('end', function () {
var ltpaCookie = res.headers['set-cookie'];
if (ltpaCookie != null) {
assert.strictEqual(302, res.statusCode);
_this.LTPAToken = ltpaCookie[0].split(';')[0].trim() + ';';
logger.debug(_this.LTPAToken);
var hheaders = {
Cookie : [_this.LTPAToken],
Accept : '*/*',
Host: _this.hostname
};
var hoptions = {
method : 'GET',
host : _this.hostname,
path : '/Unity/',
headers : hheaders
};
if(!_this.isCloud) {
hheaders.Host = _this.hostname + ':' + _this.port;
hoptions.port = _this.port;
hoptions.agent = _this.agent;
hoptions.ca = [ _this.ca ];
}
var hreq = _this.client.request(hoptions, function(hres) {
hres.on('error', function (e) {
logger.error('Failed to log in to scala server due to ' + (e && e.message ? e.message : ''));
});
hres.on('data', function (data) {});
hres.on('end', function () {
var csrfCookie = hres.headers['set-cookie'];
if (csrfCookie != null) {
assert.strictEqual(200, hres.statusCode);
_this.CSRFToken = csrfCookie[0].split(';')[0].trim();
_this.reinitInProgress = false;
logger.debug('Successfully logged into SCALA: ' + _this.hostname +'; fetched CSRF & LTPA tokens!');
_this.scheduleReauth();
} else {
logger.error('Could not log into SCALA, could not get CSRF token');
}
});
});
hreq.on('error', function(e) {
logger.error('Could not log into SCALA, due to: ' + e);
});
hreq.end();
} else {
logger.error('Could not log into SCALA, could not get LTPA token');
}
});
});
post_req.on('error', function(e) {
logger.error('Could not log into SCALA, due to: ' + e);
});
post_req.write(this.login_data);
post_req.end();
} catch (e) {
logger.error(e);
}
logger.debug('Exiting RestClient.prototype.doLogin');
}
exports.createRestClient = function (options) {
var client = new RestClient(options);
client.doLogin();
return client;
};