all files / raven-node/lib/ utils.js

93.94% Statements 93/99
85.71% Branches 54/63
100% Functions 10/10
95.7% Lines 89/93
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182              19× 19× 19× 19× 19× 19×     64×     60× 60×               60×     60×     59× 59× 59× 59× 59×         21× 20×     20× 19×             201× 201×                     204×     204× 204× 204× 204×   165×       39× 39×   38×     22×     22×     21×     21×       18×     18×   18× 201×                   201×     201×     201× 18× 18× 18×     183×           183× 183× 183× 183× 183×   183× 183×     183× 183× 183×          
'use strict';
 
var raven = require('./client');
var fs = require('fs');
var url = require('url');
var transports = require('./transports');
var path = require('path');
var lsmod = require('lsmod');
var stacktrace = require('stack-trace');
 
var protocolMap = {
  http: 80,
  https: 443
};
 
module.exports.getAuthHeader = function getAuthHeader(timestamp, api_key, api_secret) {
  var header = ['Sentry sentry_version=5'];
  header.push('sentry_timestamp=' + timestamp);
  header.push('sentry_client=raven-node/' + raven.version);
  header.push('sentry_key=' + api_key);
  header.push('sentry_secret=' + api_secret);
  return header.join(', ');
};
 
module.exports.parseDSN = function parseDSN(dsn) {
  if (!dsn) {
    // Let a falsey value return false explicitly
    return false;
  }
  try {
    var parsed = url.parse(dsn),
      response = {
        protocol: parsed.protocol.slice(0, -1),
        public_key: parsed.auth.split(':')[0],
        private_key: parsed.auth.split(':')[1],
        host: parsed.host.split(':')[0]
      };
 
    if (~response.protocol.indexOf('+')) {
      response.protocol = response.protocol.split('+')[1];
    }
 
    if (!transports.hasOwnProperty(response.protocol)) {
      throw new Error('Invalid transport');
    }
 
    var index = parsed.pathname.lastIndexOf('/');
    response.path = parsed.pathname.substr(0, index + 1);
    response.project_id = parsed.pathname.substr(index + 1);
    response.port = ~~parsed.port || protocolMap[response.protocol] || 443;
    return response;
  } catch (e) {
    throw new Error('Invalid Sentry DSN: ' + dsn);
  }
};
 
module.exports.getCulprit = function getCulprit(frame) {
  if (frame.module || frame.function)
    return (frame.module || '?') + ' at ' + (frame.function || '?');
  return '<unknown>';
};
 
var module_cache;
module.exports.getModules = function getModules() {
  if (module_cache) {
    return module_cache;
  }
 
  return module_cache = lsmod();
};
 
 
var LINES_OF_CONTEXT = 7;
 
function getFunction(line) {
  try {
    return line.getFunctionName() ||
      line.getTypeName() + '.' + (line.getMethodName() || '<anonymous>');
  } catch (e) {
    // This seems to happen sometimes when using 'use strict',
    // stemming from `getTypeName`.
    // [TypeError: Cannot read property 'constructor' of undefined]
    return '<anonymous>';
  }
}
 
var main_module = (require.main && path.dirname(require.main.filename) || process.cwd()) + '/';
 
function getModule(filename, base) {
  if (!base) base = main_module;
 
  // It's specifically a module
  var file = path.basename(filename, '.js');
  filename = path.dirname(filename);
  var n = filename.lastIndexOf('/node_modules/');
  if (n > -1) {
    // /node_modules/ is 14 chars
    return filename.substr(n + 14).replace(/\//g, '.') + ':' + file;
  }
  // Let's see if it's a part of the main module
  // To be a part of main module, it has to share the same base
  n = (filename + '/').lastIndexOf(base, 0);
  if (n === 0) {
    var module = filename.substr(base.length).replace(/\//g, '.');
    Eif (module) module += ':';
    module += file;
    return module
  }
  return file;
}
 
function parseStack(err, cb) {
  var frames = [],
    cache = {}
 
  if (!err) {
    return cb(frames);
  }
 
  var stack = stacktrace.parse(err);
 
  // check to make sure that the stack is what we need it to be.
  if (!stack || !Array.isArray(stack) || !stack.length || !stack[0].getFileName) {
    // lol, stack is fucked
    return cb(frames);
  }
 
  var callbacks = stack.length;
 
  // Sentry requires the stack trace to be from oldest to newest
  stack.reverse();
 
  stack.forEach(function(line, index) {
    var frame = {
        filename: line.getFileName() || '',
        lineno: line.getLineNumber(),
        'function': getFunction(line),
      },
      isInternal = line.isNative() ||
      frame.filename[0] !== '/' &&
      frame.filename[0] !== '.';
 
    // in_app is all that's not an internal Node function or a module within node_modules
    frame.in_app = !isInternal && !~frame.filename.indexOf('node_modules/');
 
    // Extract a module name based on the filename
    Eif (frame.filename) frame.module = getModule(frame.filename);
 
    // internal Node files are not full path names. Ignore them.
    if (isInternal) {
      frames[index] = frame;
      Iif (--callbacks === 0) cb(frames);
      return;
    }
 
    Iif (frame.filename in cache) {
      parseLines(cache[frame.filename]);
      if (--callbacks === 0) cb(frames);
      return;
    }
 
    fs.readFile(frame.filename, function(_err, file) {
      Eif (!_err) {
        file = file.toString().split('\n');
        cache[frame.filename] = file;
        parseLines(file, frame);
      }
      frames[index] = frame;
      if (--callbacks === 0) cb(frames);
    });
 
    function parseLines(lines) {
      frame.pre_context = lines.slice(Math.max(0, frame.lineno - (LINES_OF_CONTEXT + 1)), frame.lineno - 1);
      frame.context_line = lines[frame.lineno - 1];
      frame.post_context = lines.slice(frame.lineno, frame.lineno + LINES_OF_CONTEXT);
    }
  });
}
 
// expose basically for testing because I don't know what I'm doing
module.exports.parseStack = parseStack;
module.exports.getModule = getModule;