UNPKG

rollbar

Version:

Effortlessly track and debug errors in your JavaScript applications with Rollbar. This package includes advanced error tracking features and an intuitive interface to help you identify and fix issues more quickly.

212 lines (192 loc) 5.03 kB
var _ = require('../utility'); var truncation = require('../truncation'); var logger = require('./logger'); var http = require('http'); var https = require('https'); var jsonBackup = require('json-stringify-safe'); var MAX_RATE_LIMIT_INTERVAL = 60; /* * accessToken may be embedded in payload but that should not be assumed * * options: { * hostname * protocol * path * port * method * } * * params is an object containing key/value pairs to be * appended to the path as 'key=value&key=value' * * payload is an unserialized object */ function Transport() { this.rateLimitExpires = 0; } Transport.prototype.get = function ( accessToken, options, params, callback, transportFactory, ) { var t; if (!callback || !_.isFunction(callback)) { callback = function () {}; } options = options || {}; _.addParamsAndAccessTokenToPath(accessToken, options, params); options.headers = _headers(accessToken, options); if (transportFactory) { t = transportFactory(options); } else { t = _transport(options); } if (!t) { logger.error( 'Unknown transport based on given protocol: ' + options.protocol, ); return callback(new Error('Unknown transport')); } var req = t.request( options, function (resp) { this.handleResponse(resp, callback); }.bind(this), ); req.on('error', function (err) { callback(err); }); req.end(); }; Transport.prototype.post = function ( accessToken, options, payload, callback, transportFactory, ) { var t; if (!callback || !_.isFunction(callback)) { callback = function () {}; } if (_currentTime() < this.rateLimitExpires) { return callback(new Error('Exceeded rate limit')); } options = options || {}; if (!payload) { return callback(new Error('Cannot send empty request')); } var stringifyResult = truncation.truncate(payload, jsonBackup); if (stringifyResult.error) { logger.error('Problem stringifying payload. Giving up'); return callback(stringifyResult.error); } var writeData = stringifyResult.value; options.headers = _headers(accessToken, options, writeData); if (transportFactory) { t = transportFactory(options); } else { t = _transport(options); } if (!t) { logger.error( 'Unknown transport based on given protocol: ' + options.protocol, ); return callback(new Error('Unknown transport')); } var req = t.request( options, function (resp) { this.handleResponse(resp, _wrapPostCallback(callback)); }.bind(this), ); req.on('error', function (err) { callback(err); }); if (writeData) { req.write(writeData); } req.end(); }; Transport.prototype.updateRateLimit = function (resp) { var remaining = parseInt(resp.headers['x-rate-limit-remaining'] || 0); var remainingSeconds = Math.min( MAX_RATE_LIMIT_INTERVAL, resp.headers['x-rate-limit-remaining-seconds'] || 0, ); var currentTime = _currentTime(); if (resp.statusCode === 429 && remaining === 0) { this.rateLimitExpires = currentTime + remainingSeconds; } else { this.rateLimitExpires = currentTime; } }; Transport.prototype.handleResponse = function (resp, callback) { this.updateRateLimit(resp); var respData = []; resp.setEncoding('utf8'); resp.on('data', function (chunk) { respData.push(chunk); }); resp.on('end', function () { respData = respData.join(''); _parseApiResponse(respData, callback); }); }; /** Helpers **/ function _headers(accessToken, options, data) { var headers = (options && options.headers) || {}; headers['Content-Type'] = 'application/json'; if (data) { try { headers['Content-Length'] = Buffer.byteLength(data, 'utf8'); } catch (e) { logger.error('Could not get the content length of the data'); } } headers['X-Rollbar-Access-Token'] = accessToken; return headers; } function _transport(options) { return { 'http:': http, 'https:': https }[options.protocol]; } function _parseApiResponse(data, callback) { var parsedData = _.jsonParse(data); if (parsedData.error) { logger.error('Could not parse api response, err: ' + parsedData.error); return callback(parsedData.error); } data = parsedData.value; if (data.err) { logger.error('Received error: ' + data.message); return callback( new Error('Api error: ' + (data.message || 'Unknown error')), ); } callback(null, data); } function _wrapPostCallback(callback) { return function (err, data) { if (err) { return callback(err); } if (data.result && data.result.uuid) { logger.log( [ 'Successful api response.', ' Link: https://rollbar.com/occurrence/uuid/?uuid=' + data.result.uuid, ].join(''), ); } else { logger.log('Successful api response'); } callback(null, data.result); }; } function _currentTime() { return Math.floor(Date.now() / 1000); } module.exports = Transport;