<commit_before>/*
 *  Copyright 2011 Rackspace
 *
 *  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 sprintf = require('./sprintf');
var log = require('logmagic').local('deploy.helpers.misc');
var spawn = require('child_process').spawn;

/**
 * Get unix timestamp.
 * @param {?Date} date Date object.
 * @return {Number} unix timestamp.
 */
exports.getUnixTimestamp = function getUnixTimestamp(date) {
  date = date || new Date();
  date = date.getTime();
  return Math.round(date / 1000);
};

/** Spawn a subprocess and issue the callback.
 * @param {Array} cmd Command and Arguments.
 * @param {Options} spawnOpts Options object passed to the spawn command.
 * @param {Function} callback Completion callback(err, stdout, stderr).
 */
exports.spawn = function(cmd, spawnOpts, callback) {
  var args,
      diff,
      proc,
      resultString = '',
      errString = '',
      cmdStr;

  // Copy command so we don't override the parent
  cmd = cmd.slice();
  args = cmd.splice(1);

  cmdStr = sprintf('%s %s', cmd[0], args.join(' '));
  log.info('Executing Command', cmdStr);

  proc = spawn(cmd[0], args, spawnOpts);
  proc.stdout.on('data', function(data) {
    resultString += data;
  });
  proc.stderr.on('data', function(data) {
    errString += data;
  });
  proc.on('exit', function(code) {
    var err;

    log.info('Command finished', {'cmd': cmdStr, 'code': code, 'stdout': resultString, 'stderr': errString});

    if (code) {
      err = new Error(sprintf('Failed command %s:\nstderr:\n%s\nstdout:\n%s', cmdStr, errString, resultString));
    }

    callback(err, resultString, errString);
  });
};

/**
 * Execute a command using a task's baton and args to log and honor dryrun.
 * @param {Object} baton A task's baton with a logger.
 * @param {Object} args A task's args which may contain 'dryrun'.
 * @param {Array} cmd The command and its arguments.
 * @param {Object} options An options object to pass to spawn.
 * @param {Function} callback A callback fired with (err, stdout, stderr).
 */
exports.taskSpawn = function(baton, args, cmd, options, callback) {
  var cmdStr = cmd.join(' '),
      start = Date.now();

  if (args.dryrun) {
    baton.log.infof('dry run, skipping command: ${cmd}', {
      cmd: cmdStr
    });
    callback();
    return;
  }

  baton.log.infof('executing command: ${cmd}', {
    cmd: cmdStr
  });

  exports.spawn(cmd, options, function(err, stdout, stderr) {
    var took = (Date.now() - start) / 1000;

    if (err) {
      baton.log.errorf('error executing command: ${cmd}', {
        cmd: cmdStr,
        err: err,
        stdout: stdout,
        stderr: stderr,
        took: took
      });
    } else {
      baton.log.infof('command successful: ${cmd}', {
        cmd: cmdStr,
        stdout: stdout,
        stderr: stderr,
        took: took
      });
    }
    callback(err, stdout, stderr);
  });
};

exports.randstr = function(len) {
  var chars, r, x;

  chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
  r = [];

  for (x = 0; x < len; x++) {
    r.push(chars[exports.getRandomInt(0, chars.length - 1)]);
  }

  return r.join('');
};

exports.getRandomInt = function(min, max) {
  return Math.floor(Math.random() * (max - min + 1)) + min;
};

exports.merge = function(a, b) {
  var c = {}, attrname;

  for (attrname in a) {
    if (a.hasOwnProperty(attrname)) {
      c[attrname] = a[attrname];
    }
  }
  for (attrname in b) {
    if (b.hasOwnProperty(attrname)) {
      c[attrname] = b[attrname];
    }
  }
  return c;
};

exports.copyEnv = function() {
  var env = {}, key;

  for (key in process.env) {
    env[key] = process.env[key];
  }

  return env;
};
<commit_msg>Fix the log message.<commit_after>/*
 *  Copyright 2011 Rackspace
 *
 *  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 sprintf = require('./sprintf');
var log = require('logmagic').local('deploy.helpers.misc');
var spawn = require('child_process').spawn;

/**
 * Get unix timestamp.
 * @param {?Date} date Date object.
 * @return {Number} unix timestamp.
 */
exports.getUnixTimestamp = function getUnixTimestamp(date) {
  date = date || new Date();
  date = date.getTime();
  return Math.round(date / 1000);
};

/** Spawn a subprocess and issue the callback.
 * @param {Array} cmd Command and Arguments.
 * @param {Options} spawnOpts Options object passed to the spawn command.
 * @param {Function} callback Completion callback(err, stdout, stderr).
 */
exports.spawn = function(cmd, spawnOpts, callback) {
  var args,
      diff,
      proc,
      resultString = '',
      errString = '',
      cmdStr;

  // Copy command so we don't override the parent
  cmd = cmd.slice();
  args = cmd.splice(1);

  cmdStr = sprintf('%s %s', cmd[0], args.join(' '));
  log.info('Executing Command', {'cmd': cmdStr});

  proc = spawn(cmd[0], args, spawnOpts);
  proc.stdout.on('data', function(data) {
    resultString += data;
  });
  proc.stderr.on('data', function(data) {
    errString += data;
  });
  proc.on('exit', function(code) {
    var err;

    log.info('Command finished', {'cmd': cmdStr, 'code': code, 'stdout': resultString, 'stderr': errString});

    if (code) {
      err = new Error(sprintf('Failed command %s:\nstderr:\n%s\nstdout:\n%s', cmdStr, errString, resultString));
    }

    callback(err, resultString, errString);
  });
};

/**
 * Execute a command using a task's baton and args to log and honor dryrun.
 * @param {Object} baton A task's baton with a logger.
 * @param {Object} args A task's args which may contain 'dryrun'.
 * @param {Array} cmd The command and its arguments.
 * @param {Object} options An options object to pass to spawn.
 * @param {Function} callback A callback fired with (err, stdout, stderr).
 */
exports.taskSpawn = function(baton, args, cmd, options, callback) {
  var cmdStr = cmd.join(' '),
      start = Date.now();

  if (args.dryrun) {
    baton.log.infof('dry run, skipping command: ${cmd}', {
      cmd: cmdStr
    });
    callback();
    return;
  }

  baton.log.infof('executing command: ${cmd}', {
    cmd: cmdStr
  });

  exports.spawn(cmd, options, function(err, stdout, stderr) {
    var took = (Date.now() - start) / 1000;

    if (err) {
      baton.log.errorf('error executing command: ${cmd}', {
        cmd: cmdStr,
        err: err,
        stdout: stdout,
        stderr: stderr,
        took: took
      });
    } else {
      baton.log.infof('command successful: ${cmd}', {
        cmd: cmdStr,
        stdout: stdout,
        stderr: stderr,
        took: took
      });
    }
    callback(err, stdout, stderr);
  });
};

exports.randstr = function(len) {
  var chars, r, x;

  chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
  r = [];

  for (x = 0; x < len; x++) {
    r.push(chars[exports.getRandomInt(0, chars.length - 1)]);
  }

  return r.join('');
};

exports.getRandomInt = function(min, max) {
  return Math.floor(Math.random() * (max - min + 1)) + min;
};

exports.merge = function(a, b) {
  var c = {}, attrname;

  for (attrname in a) {
    if (a.hasOwnProperty(attrname)) {
      c[attrname] = a[attrname];
    }
  }
  for (attrname in b) {
    if (b.hasOwnProperty(attrname)) {
      c[attrname] = b[attrname];
    }
  }
  return c;
};

exports.copyEnv = function() {
  var env = {}, key;

  for (key in process.env) {
    env[key] = process.env[key];
  }

  return env;
};
<|endoftext|>


<commit_before>
// handle some git configuration for windows

exports.spawn = spawnGit
exports.chainableExec = chainableExec
exports.whichAndExec = whichAndExec

var exec = require("child_process").execFile
  , spawn = require("child_process").spawn
  , npm = require("../npm.js")
  , which = require("which")
  , git = npm.config.get("git")

function prefixGitArgs() {
  return process.platform === "win32" ? ["-c", "core.longpaths=true"] : []
}

function execGit(args, options, cb) {
  return exec(git, prefixGitArgs().concat(args || []), options, cb)
}

function spawnGit(args, options, cb) {
  return spawn(git, prefixGitArgs().concat(args || []), options)
}

function chainableExec() {
  var args = Array.prototype.slice.call(arguments)
  return [gitExec].concat(args)
}

function whichGit(cb) {
  return which(git, cb)
}

function whichAndExec(args, options, cb) {
  // check for git
  whichGit(function (err) {
    if (err) {
      err.code = "ENOGIT"
      return cb(err)
    }

    execGit(args, options, cb)
  })
}
<commit_msg>typo in lib/utils/git.js<commit_after>
// handle some git configuration for windows

exports.spawn = spawnGit
exports.chainableExec = chainableExec
exports.whichAndExec = whichAndExec

var exec = require("child_process").execFile
  , spawn = require("child_process").spawn
  , npm = require("../npm.js")
  , which = require("which")
  , git = npm.config.get("git")

function prefixGitArgs() {
  return process.platform === "win32" ? ["-c", "core.longpaths=true"] : []
}

function execGit(args, options, cb) {
  return exec(git, prefixGitArgs().concat(args || []), options, cb)
}

function spawnGit(args, options, cb) {
  return spawn(git, prefixGitArgs().concat(args || []), options)
}

function chainableExec() {
  var args = Array.prototype.slice.call(arguments)
  return [execGit].concat(args)
}

function whichGit(cb) {
  return which(git, cb)
}

function whichAndExec(args, options, cb) {
  // check for git
  whichGit(function (err) {
    if (err) {
      err.code = "ENOGIT"
      return cb(err)
    }

    execGit(args, options, cb)
  })
}
<|endoftext|>


<commit_before>
/*
log levels:
0,1,2,3
verbose,info,warn,error

Default setting for logs is "info"
Default setting to show is "info"

Possible values of level/loglevel:
silly,verbose,info,warn,error,win,silent

silent quiets everything

*/

module.exports = log

var doColor
try {
  var stdio = require("stdio")
  doColor = stdio.isStderrATTY()
} catch (ex) {
  doColor = true
}

function colorize (msg, color) {
  if (!msg) return ""
  if (!doColor) return msg
  return "\033["+color+"m"+msg+"\033[0m"
}

var l = -1
  , LEVEL = { silly   : l++
            , verbose : l++
            , info    : l++
            , WARN    : l++
            , "ERR!"  : l++
            , ERROR   : "ERR!"
            , ERR     : "ERR!"
            , win     : 0x15AAC5
            , silent  : 0xDECAFBAD
            }
  , COLOR = {}
  , SHOWLEVEL
log.LEVEL = LEVEL
Object.keys(LEVEL).forEach(function (l) {
  if (typeof LEVEL[l] === "string") LEVEL[l] = LEVEL[LEVEL[l]]
  else LEVEL[LEVEL[l]] = l
  LEVEL[l.toLowerCase()] = LEVEL[l]
  if (l === "silent") return
  log[l] = log[l.toLowerCase()] =
    function (msg, pref, cb) { return log(msg, pref, l, cb) }
})
COLOR[LEVEL.silly] = 30
COLOR[LEVEL.verbose] = 34
COLOR[LEVEL.info] = 32
COLOR[LEVEL.warn] = 33
COLOR[LEVEL.error] = 31
for (var c in COLOR) COLOR[LEVEL[c]] = COLOR[c]
COLOR.npm = "37;40"
COLOR.pref = 35

var logBuffer = []
  , ini = require("./ini")
  , waitForConfig
log.waitForConfig = function () { waitForConfig = true }

// now the required stuff has been loaded,
// so the transitive module dep will work
var sys = require("./sys")
  , npm = require("../../npm")

Object.defineProperty(log, "level",
  { get : function () {
      if (typeof SHOWLEVEL !== "undefined") return SHOWLEVEL
      var show = npm.config && npm.config.get("loglevel") || ''
      show = show.split(",")[0]
      if (!isNaN(show)) show = +show
      else if (!LEVEL.hasOwnProperty(show)) {
        sys.error("Invalid loglevel config: "+JSON.stringify(show))
        show = "info"
      }
      if (isNaN(show)) show = LEVEL[show]
      else show = +show
      if (!waitForConfig || ini.resulved) SHOWLEVEL = show
      return show
    }
  , set : function (l) {
      SHOWLEVEL = undefined
      npm.config.set("showlevel", l)
    }
  })
function log (msg, pref, level, cb) {
  if (typeof level === "function") cb = level, level = null
  var show = log.level
  if (show === LEVEL.silent) return cb && cb()
  if (level == null) level = LEVEL.info
  if (isNaN(level)) level = LEVEL[level]
  else level = +level
  if (typeof msg !== "string" && !(msg instanceof Error)) {
    msg = sys.inspect(msg, 0, 4)
  }
  if (msg instanceof Error) {
    msg = msg.stack || msg.toString()
    level = LEVEL.error
  }
  if (!ini.resolved && waitForConfig) {
    return logBuffer.push([msg, pref, level])
  }
  if (logBuffer.length && !logBuffer.discharging) {
    logBuffer.push([msg, pref, level])
    logBuffer.discharging = true
    logBuffer.forEach(function (l) { log.apply(null, l) })
    logBuffer.length = 0
    delete logBuffer.discharging
    return
  }
  log.level = show

  if (!isFinite(level) || level < show) return cb && cb()
  // console.error("level, showlevel, show", level, show, (level >= show))
  pref = colorize("npm", COLOR.npm)
       + (COLOR[level] ? " "+colorize(
           (LEVEL[level]+spaces).substr(0,4), COLOR[level]) : "")
       + (pref && COLOR.pref ? " " + colorize(pref, COLOR.pref) : "")
  if (msg.indexOf("\n") !== -1) {
    msg = msg.split(/\n/).join("\n"+pref+" ")
  }
  msg = pref+" "+msg
  sys.error(msg)
  cb && cb()
}
var spaces = "    "
log.er = function (cb, msg) {
  if (!msg) throw new Error(
    "Why bother logging it if you're not going to print a message?")
  return function (er) {
    if (er) log.error(msg)
    cb.apply(this, arguments)
  }
}
<commit_msg>Make it easier to track down "undefined" logs with unhelpful prefixes<commit_after>
/*
log levels:
0,1,2,3
verbose,info,warn,error

Default setting for logs is "info"
Default setting to show is "info"

Possible values of level/loglevel:
silly,verbose,info,warn,error,win,silent

silent quiets everything

*/

module.exports = log

var doColor
try {
  var stdio = require("stdio")
  doColor = stdio.isStderrATTY()
} catch (ex) {
  doColor = true
}

function colorize (msg, color) {
  if (!msg) return ""
  if (!doColor) return msg
  return "\033["+color+"m"+msg+"\033[0m"
}

var l = -1
  , LEVEL = { silly   : l++
            , verbose : l++
            , info    : l++
            , WARN    : l++
            , "ERR!"  : l++
            , ERROR   : "ERR!"
            , ERR     : "ERR!"
            , win     : 0x15AAC5
            , silent  : 0xDECAFBAD
            }
  , COLOR = {}
  , SHOWLEVEL
log.LEVEL = LEVEL
Object.keys(LEVEL).forEach(function (l) {
  if (typeof LEVEL[l] === "string") LEVEL[l] = LEVEL[LEVEL[l]]
  else LEVEL[LEVEL[l]] = l
  LEVEL[l.toLowerCase()] = LEVEL[l]
  if (l === "silent") return
  log[l] = log[l.toLowerCase()] =
    function (msg, pref, cb) { return log(msg, pref, l, cb) }
})
COLOR[LEVEL.silly] = 30
COLOR[LEVEL.verbose] = 34
COLOR[LEVEL.info] = 32
COLOR[LEVEL.warn] = 33
COLOR[LEVEL.error] = 31
for (var c in COLOR) COLOR[LEVEL[c]] = COLOR[c]
COLOR.npm = "37;40"
COLOR.pref = 35

var logBuffer = []
  , ini = require("./ini")
  , waitForConfig
log.waitForConfig = function () { waitForConfig = true }

// now the required stuff has been loaded,
// so the transitive module dep will work
var sys = require("./sys")
  , npm = require("../../npm")

Object.defineProperty(log, "level",
  { get : function () {
      if (typeof SHOWLEVEL !== "undefined") return SHOWLEVEL
      var show = npm.config && npm.config.get("loglevel") || ''
      show = show.split(",")[0]
      if (!isNaN(show)) show = +show
      else if (!LEVEL.hasOwnProperty(show)) {
        sys.error("Invalid loglevel config: "+JSON.stringify(show))
        show = "info"
      }
      if (isNaN(show)) show = LEVEL[show]
      else show = +show
      if (!waitForConfig || ini.resulved) SHOWLEVEL = show
      return show
    }
  , set : function (l) {
      SHOWLEVEL = undefined
      npm.config.set("showlevel", l)
    }
  })
function log (msg, pref, level, cb) {
  if (typeof level === "function") cb = level, level = null
  var show = log.level
  if (show === LEVEL.silent) return cb && cb()
  if (level == null) level = LEVEL.info
  if (isNaN(level)) level = LEVEL[level]
  else level = +level
  // logging just undefined is almost never the right thing.
  // a lot of these are kicking around throughout the codebase
  // with relatively unhelpful prefixes.
  if (msg === undefined) {
    msg = [msg, (new Error).stack.split(/\n/).slice(2)]
  }
  if (typeof msg !== "string" && !(msg instanceof Error)) {
    msg = sys.inspect(msg, 0, 4)
  }
  if (msg instanceof Error) {
    msg = msg.stack || msg.toString()
    level = LEVEL.error
  }
  if (!ini.resolved && waitForConfig) {
    return logBuffer.push([msg, pref, level])
  }
  if (logBuffer.length && !logBuffer.discharging) {
    logBuffer.push([msg, pref, level])
    logBuffer.discharging = true
    logBuffer.forEach(function (l) { log.apply(null, l) })
    logBuffer.length = 0
    delete logBuffer.discharging
    return
  }
  log.level = show

  if (!isFinite(level) || level < show) return cb && cb()
  // console.error("level, showlevel, show", level, show, (level >= show))
  pref = colorize("npm", COLOR.npm)
       + (COLOR[level] ? " "+colorize(
           (LEVEL[level]+spaces).substr(0,4), COLOR[level]) : "")
       + (pref && COLOR.pref ? " " + colorize(pref, COLOR.pref) : "")
  if (msg.indexOf("\n") !== -1) {
    msg = msg.split(/\n/).join("\n"+pref+" ")
  }
  msg = pref+" "+msg
  sys.error(msg)
  cb && cb()
}
var spaces = "    "
log.er = function (cb, msg) {
  if (!msg) throw new Error(
    "Why bother logging it if you're not going to print a message?")
  return function (er) {
    if (er) log.error(msg)
    cb.apply(this, arguments)
  }
}
<|endoftext|>


<commit_before>var Validator = exports.Validator = function() {}

Validator.prototype.check = function(str, fail_msg) {
    this.str = String(str || '');
    this.msg = fail_msg;
    return this;
}

//Create some aliases - may help code readability
Validator.prototype.validate = Validator.prototype.check;
Validator.prototype.assert = Validator.prototype.check;

Validator.prototype.error = function(msg) {
    throw msg;
    return this;
}

Validator.prototype.isEmail = function() {
    if (!this.str.match(/^(?:[\w\!\#\$\%\&\'\*\+\-\/\=\?\^\`\{\|\}\~]+\.)*[\w\!\#\$\%\&\'\*\+\-\/\=\?\^\`\{\|\}\~]+@(?:(?:(?:[a-zA-Z0-9](?:[a-zA-Z0-9\-](?!\.)){0,61}[a-zA-Z0-9]?\.)+[a-zA-Z0-9](?:[a-zA-Z0-9\-](?!$)){0,61}[a-zA-Z0-9]?)|(?:\[(?:(?:[01]?\d{1,2}|2[0-4]\d|25[0-5])\.){3}(?:[01]?\d{1,2}|2[0-4]\d|25[0-5])\]))$/)) {
        this.error(this.msg || 'Invalid email');
    }
    return this;
}

Validator.prototype.isUrl = function() {
    if (!this.str.match(/^(?:(?:ht|f)tp(?:s?)\:\/\/|~\/|\/)?(?:\w+:\w+@)?((?:(?:[-\w\d{1-3}]+\.)+(?:com|org|net|gov|mil|biz|info|mobi|name|aero|jobs|edu|co\.uk|ac\.uk|it|fr|tv|museum|asia|local|travel|[a-z]{2})?)|((\b25[0-5]\b|\b[2][0-4][0-9]\b|\b[0-1]?[0-9]?[0-9]\b)(\.(\b25[0-5]\b|\b[2][0-4][0-9]\b|\b[0-1]?[0-9]?[0-9]\b)){3}))(?::[\d]{1,5})?(?:(?:(?:\/(?:[-\w~!$+|.,=]|%[a-f\d]{2})+)+|\/)+|\?|#)?(?:(?:\?(?:[-\w~!$+|.,*:]|%[a-f\d{2}])+=?(?:[-\w~!$+|.,*:=]|%[a-f\d]{2})*)(?:&(?:[-\w~!$+|.,*:]|%[a-f\d{2}])+=?(?:[-\w~!$+|.,*:=]|%[a-f\d]{2})*)*)*(?:#(?:[-\w~!$ |\/.,*:;=]|%[a-f\d]{2})*)?$/)) {
        this.error(this.msg || 'Invalid URL');
    }
    return this;
}

Validator.prototype.isIP = function() {
    if (!this.str.match(/^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/)) {
        this.error(this.msg || 'Invalid IP');
    }
    return this;
}

Validator.prototype.isAlpha = function() {
    if (!this.str.match(/^[a-zA-Z]+$/)) {
        this.error(this.msg || 'Invalid characters');
    }
    return this;
}

Validator.prototype.isAlphanumeric = function() {
    if (!this.str.match(/^[a-zA-Z0-9]+$/)) {
        this.error(this.msg || 'Invalid characters');
    }
    return this;
}

Validator.prototype.isNumeric = function() {
    if (!this.str.match(/^-?[0-9]+$/)) {
        this.error(this.msg || 'Invalid number');
    }
    return this;
}

Validator.prototype.isLowercase = function() {
    if (!this.str.match(/^[a-z0-9]+$/)) {
        this.error(this.msg || 'Invalid characters');
    }
    return this;
}

Validator.prototype.isUppercase = function() {
    if (!this.str.match(/^[A-Z0-9]+$/)) {
        this.error(this.msg || 'Invalid characters');
    }
    return this;
}

Validator.prototype.isInt = function() {
    if (!this.str.match(/^(?:-?(?:0|[1-9][0-9]*))$/)) {
        this.error(this.msg || 'Invalid integer');
    }
    return this;
}

Validator.prototype.isDecimal = function() {
    if (!this.str.match(/^(?:-?(?:0|[1-9][0-9]*))?(?:\.[0-9]*)?$/)) {
        this.error(this.msg || 'Invalid decimal');
    }
    return this;
}

Validator.prototype.isFloat = function() {
    return this.isDecimal();
}

Validator.prototype.notNull = function() {
    if (this.str === '') {
        this.error(this.msg || 'Invalid characters');
    }
    return this;
}

Validator.prototype.isNull = function() {
    if (this.str !== '') {
        this.error(this.msg || 'Invalid characters');
    }
    return this;
}

Validator.prototype.notEmpty = function() {
    if (this.str.match(/^[\s\t\r\n]*$/)) {
        this.error(this.msg || 'String is whitespace');
    }
    return this;
}

Validator.prototype.equals = function(equals) {
    if (this.str != equals) {
        this.error(this.msg || 'Not equal');
    }
    return this;
}

Validator.prototype.contains = function(str) {
    if (this.str.indexOf(str) === -1) {
        this.error(this.msg || 'Invalid characters');
    }
    return this;
}

Validator.prototype.notContains = function(str) {
    if (this.str.indexOf(str) >= 0) {
        this.error(this.msg || 'Invalid characters');
    }
    return this;
}

Validator.prototype.regex = function(pattern, modifiers) {
    if (typeof pattern !== 'function') {
        pattern = new RegExp(pattern, modifiers || '');
    }
    if (! this.str.match(pattern)) {
        this.error(this.msg || 'Invalid characters');
    }
    return this;
}

Validator.prototype.notRegex = function(pattern, modifiers) {
    if (typeof pattern !== 'function') {
        pattern = new RegExp(pattern, modifiers || '');
    }
    if (this.str.match(pattern)) {
        this.error(this.msg || 'Invalid characters');
    }
    return this;
}

Validator.prototype.len = function(min, max) {
    if (this.str.length < min) {
        this.error(this.msg || 'String is too large');
    }
    if (typeof max !== undefined && this.str.length > max) {
        this.error(this.msg || 'String is too small');
    }
    return this;
}
<commit_msg>Fixed error message with len()<commit_after>var Validator = exports.Validator = function() {}

Validator.prototype.check = function(str, fail_msg) {
    this.str = String(str || '');
    this.msg = fail_msg;
    return this;
}

//Create some aliases - may help code readability
Validator.prototype.validate = Validator.prototype.check;
Validator.prototype.assert = Validator.prototype.check;

Validator.prototype.error = function(msg) {
    throw msg;
    return this;
}

Validator.prototype.isEmail = function() {
    if (!this.str.match(/^(?:[\w\!\#\$\%\&\'\*\+\-\/\=\?\^\`\{\|\}\~]+\.)*[\w\!\#\$\%\&\'\*\+\-\/\=\?\^\`\{\|\}\~]+@(?:(?:(?:[a-zA-Z0-9](?:[a-zA-Z0-9\-](?!\.)){0,61}[a-zA-Z0-9]?\.)+[a-zA-Z0-9](?:[a-zA-Z0-9\-](?!$)){0,61}[a-zA-Z0-9]?)|(?:\[(?:(?:[01]?\d{1,2}|2[0-4]\d|25[0-5])\.){3}(?:[01]?\d{1,2}|2[0-4]\d|25[0-5])\]))$/)) {
        this.error(this.msg || 'Invalid email');
    }
    return this;
}

Validator.prototype.isUrl = function() {
    if (!this.str.match(/^(?:(?:ht|f)tp(?:s?)\:\/\/|~\/|\/)?(?:\w+:\w+@)?((?:(?:[-\w\d{1-3}]+\.)+(?:com|org|net|gov|mil|biz|info|mobi|name|aero|jobs|edu|co\.uk|ac\.uk|it|fr|tv|museum|asia|local|travel|[a-z]{2})?)|((\b25[0-5]\b|\b[2][0-4][0-9]\b|\b[0-1]?[0-9]?[0-9]\b)(\.(\b25[0-5]\b|\b[2][0-4][0-9]\b|\b[0-1]?[0-9]?[0-9]\b)){3}))(?::[\d]{1,5})?(?:(?:(?:\/(?:[-\w~!$+|.,=]|%[a-f\d]{2})+)+|\/)+|\?|#)?(?:(?:\?(?:[-\w~!$+|.,*:]|%[a-f\d{2}])+=?(?:[-\w~!$+|.,*:=]|%[a-f\d]{2})*)(?:&(?:[-\w~!$+|.,*:]|%[a-f\d{2}])+=?(?:[-\w~!$+|.,*:=]|%[a-f\d]{2})*)*)*(?:#(?:[-\w~!$ |\/.,*:;=]|%[a-f\d]{2})*)?$/)) {
        this.error(this.msg || 'Invalid URL');
    }
    return this;
}

Validator.prototype.isIP = function() {
    if (!this.str.match(/^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/)) {
        this.error(this.msg || 'Invalid IP');
    }
    return this;
}

Validator.prototype.isAlpha = function() {
    if (!this.str.match(/^[a-zA-Z]+$/)) {
        this.error(this.msg || 'Invalid characters');
    }
    return this;
}

Validator.prototype.isAlphanumeric = function() {
    if (!this.str.match(/^[a-zA-Z0-9]+$/)) {
        this.error(this.msg || 'Invalid characters');
    }
    return this;
}

Validator.prototype.isNumeric = function() {
    if (!this.str.match(/^-?[0-9]+$/)) {
        this.error(this.msg || 'Invalid number');
    }
    return this;
}

Validator.prototype.isLowercase = function() {
    if (!this.str.match(/^[a-z0-9]+$/)) {
        this.error(this.msg || 'Invalid characters');
    }
    return this;
}

Validator.prototype.isUppercase = function() {
    if (!this.str.match(/^[A-Z0-9]+$/)) {
        this.error(this.msg || 'Invalid characters');
    }
    return this;
}

Validator.prototype.isInt = function() {
    if (!this.str.match(/^(?:-?(?:0|[1-9][0-9]*))$/)) {
        this.error(this.msg || 'Invalid integer');
    }
    return this;
}

Validator.prototype.isDecimal = function() {
    if (!this.str.match(/^(?:-?(?:0|[1-9][0-9]*))?(?:\.[0-9]*)?$/)) {
        this.error(this.msg || 'Invalid decimal');
    }
    return this;
}

Validator.prototype.isFloat = function() {
    return this.isDecimal();
}

Validator.prototype.notNull = function() {
    if (this.str === '') {
        this.error(this.msg || 'Invalid characters');
    }
    return this;
}

Validator.prototype.isNull = function() {
    if (this.str !== '') {
        this.error(this.msg || 'Invalid characters');
    }
    return this;
}

Validator.prototype.notEmpty = function() {
    if (this.str.match(/^[\s\t\r\n]*$/)) {
        this.error(this.msg || 'String is whitespace');
    }
    return this;
}

Validator.prototype.equals = function(equals) {
    if (this.str != equals) {
        this.error(this.msg || 'Not equal');
    }
    return this;
}

Validator.prototype.contains = function(str) {
    if (this.str.indexOf(str) === -1) {
        this.error(this.msg || 'Invalid characters');
    }
    return this;
}

Validator.prototype.notContains = function(str) {
    if (this.str.indexOf(str) >= 0) {
        this.error(this.msg || 'Invalid characters');
    }
    return this;
}

Validator.prototype.regex = function(pattern, modifiers) {
    if (typeof pattern !== 'function') {
        pattern = new RegExp(pattern, modifiers);
    }
    if (! this.str.match(pattern)) {
        this.error(this.msg || 'Invalid characters');
    }
    return this;
}

Validator.prototype.notRegex = function(pattern, modifiers) {
    if (typeof pattern !== 'function') {
        pattern = new RegExp(pattern, modifiers);
    }
    if (this.str.match(pattern)) {
        this.error(this.msg || 'Invalid characters');
    }
    return this;
}

Validator.prototype.len = function(min, max) {
    if (this.str.length < min) {
        this.error(this.msg || 'String is too small');
    }
    if (typeof max !== undefined && this.str.length > max) {
        this.error(this.msg || 'String is too large');
    }
    return this;
}
<|endoftext|>


<commit_before>"use strict";

var HTTPError = require('./rbUtil').HTTPError;
var constructAjv = require('ajv');

/**
 * Mapping of `param.in` field to the name of a request part.
 *
 * @const
 * @type {{path: string, query: string, header: string, formData: string, body: string}}
 */
var inMapping = {
    path: 'params',
    query: 'query',
    header: 'headers',
    formData: 'body',
    body: 'body'
};

/**
 * Supported field validators.
 *
 * @const
 * @type {string[]}
 */
var supportedValidators = ['maximum',
    'exclusiveMaximum',
    'minimum',
    'exclusiveMinimum',
    'maxLength',
    'minLength',
    'pattern',
    'maxItems',
    'minItems',
    'uniqueItems',
    'enum',
    'multipleOf'];

/**
 * Constructs a request validator, according to swagger parameters specification.
 * A returned object contains a single `validate(req)` function.
 *
 * @param parameters {Array} swagger parameters spec
 * @constructor
 */
var Validator = function(parameters) {
    this._ajv = constructAjv();
    this._typeCoercionFunc = this._createTypeCoercionFunc(parameters);
    this._validatorFunc = this._ajv.compile(this._convertToJsonSchema(parameters));
};

/**
 * Converts a list of parameters from a swagger spec
 * to JSON-schema for a request
 *
 * @param parameters list of params
 * @returns {Object} JSON schema
 * @private
 */
Validator.prototype._convertToJsonSchema = function(parameters) {
    var schema = {
        type: 'object',
        properties: {}
    };

    parameters.forEach(function(param) {
        if (param.in !== 'body') {
            if (!schema.properties[inMapping[param.in]]) {
                schema.properties[inMapping[param.in]] = {
                    type: 'object',
                    properties: {}
                };
                // 'required' array must have at least one element according to json-schema spec,
                // se we can't preinitialize it.
                schema.required = schema.required || [];
                if (schema.required.indexOf(inMapping[param.in]) < 0) {
                    schema.required.push(inMapping[param.in]);
                }
            }

            var reqPartSchema = schema.properties[inMapping[param.in]];
            var paramSchema = { type: param.type };
            supportedValidators.forEach(function(validator) {
                paramSchema[validator] = param[validator];
            });
            reqPartSchema.properties[param.name] = paramSchema;
            if (param.required) {
                reqPartSchema.required = reqPartSchema.required || [];
                reqPartSchema.required.push(param.name);
            }
        } else {
            if (param.schema) {
                schema.properties.body = param.schema;
            } else {
                schema.properties.body = {
                    type: 'object'
                };
            }
            if (param.required) {
                schema.required = schema.required || [];
                schema.required.push('body');
            }
        }
    });

    return schema;
};

/**
 * Creates a function, that tries to coerce types of parameters in the
 * incoming request, according to the provided parameters specification.
 *
 * @param parameters {Array} parameters swagger specification
 * @returns {Function(req, HTTPError)} cersion function
 * @private
 */
Validator.prototype._createTypeCoercionFunc = function(parameters) {
    var code = '';
    parameters.forEach(function(param) {
        if (param.type === 'string') {
            // Don't need to process strings
            return;
        }
        var paramAccessor = 'req.' + inMapping[param.in] + '["' + param.name + '"]';
        var paramCoercionCode = 'if (typeof ' + paramAccessor + " === 'string') {\n";
        var errorNotifier;
        switch (param.type) {
            case 'integer':
                errorNotifier = 'throw new HTTPError({status:400,body:{type:"bad_request",'
                    + ' title:"Invalid parameters", detail: "data.'
                    + inMapping[param.in] + '.' + param.name + ' should be an integer"}});\n';
                paramCoercionCode += paramAccessor + ' = parseInt(' + paramAccessor + ');\n'
                    + 'if (!Number.isInteger(' + paramAccessor + ')) {\n' + errorNotifier + '}\n';
                break;
            case 'number':
                errorNotifier = 'throw new HTTPError({status:400,body:{type:"bad_request",'
                    + ' title:"Invalid parameters", detail: "data.'
                    + inMapping[param.in] + '.' + param.name + ' should be a number"}});\n';
                paramCoercionCode += paramAccessor + ' = parseFloat(' + paramAccessor + ');\n'
                    + 'if (Number.isNaN(' + paramAccessor + ')) {\n' + errorNotifier + '}\n';
                break;
            case 'boolean':
                errorNotifier = 'throw new HTTPError({status:400,body:{type:"bad_request",'
                    + ' title:"Invalid parameters", detail: "data.'
                    + inMapping[param.in] + '.' + param.name + ' should be a boolean.'
                    + ' true|false|1|0 is accepted as a boolean."}});\n';
                paramCoercionCode += 'if(!/^true|false|1|0$/i.test('
                    + paramAccessor + ' + "")) {\n'
                    + errorNotifier + '}\n'
                    + paramAccessor + ' = /^true|1$/i.test('
                    + paramAccessor + ' + "");\n';
        }
        paramCoercionCode += "}\n";

        if (!param.required) {
            // If parameter is not required, don't try to coerce "undefined"
            paramCoercionCode = 'if (' + paramAccessor + ' !== undefined) {\n'
                + paramCoercionCode + '}\n';
        }

        code += paramCoercionCode;
    });
    if (code && code.trim()) {
        code += '\nreturn req;\n';
        /*jshint evil:true */
        return new Function('req', 'HTTPError', code);
    } else {
        return undefined;
    }
};

/**
 * Validates a request. In case of an error, throws HTTPError with 400 code
 * @param req {Object} a request object to validate.
 */
Validator.prototype.validate = function(req) {
    if (this._typeCoercionFunc) {
        req = this._typeCoercionFunc(req, HTTPError);
    }
    if (!this._validatorFunc(req)) {
        throw new HTTPError({
            status: 400,
            body: {
                type: 'bad_request',
                title: 'Invalid parameters',
                detail: this._ajv.errorsText(this._validatorFunc.errors),
                req: req
            }
        });
    }
};

module.exports = Validator;
<commit_msg>Don't coerce types in JSON body<commit_after>"use strict";

var HTTPError = require('./rbUtil').HTTPError;
var constructAjv = require('ajv');

/**
 * Mapping of `param.in` field to the name of a request part.
 *
 * @const
 * @type {{path: string, query: string, header: string, formData: string, body: string}}
 */
var inMapping = {
    path: 'params',
    query: 'query',
    header: 'headers',
    formData: 'body',
    body: 'body'
};

/**
 * Supported field validators.
 *
 * @const
 * @type {string[]}
 */
var supportedValidators = ['maximum',
    'exclusiveMaximum',
    'minimum',
    'exclusiveMinimum',
    'maxLength',
    'minLength',
    'pattern',
    'maxItems',
    'minItems',
    'uniqueItems',
    'enum',
    'multipleOf'];

/**
 * Constructs a request validator, according to swagger parameters specification.
 * A returned object contains a single `validate(req)` function.
 *
 * @param parameters {Array} swagger parameters spec
 * @constructor
 */
var Validator = function(parameters) {
    this._ajv = constructAjv();
    this._paramCoercionFunc = this._createTypeCoercionFunc(parameters.filter(function(p) {
        return p.name !== 'body';
    }));
    this._bodyCoercionFunc = this._createTypeCoercionFunc(parameters.filter(function(p) {
        return p.name === 'body';
    }));
    this._validatorFunc = this._ajv.compile(this._convertToJsonSchema(parameters));
};

/**
 * Converts a list of parameters from a swagger spec
 * to JSON-schema for a request
 *
 * @param parameters list of params
 * @returns {Object} JSON schema
 * @private
 */
Validator.prototype._convertToJsonSchema = function(parameters) {
    var schema = {
        type: 'object',
        properties: {}
    };

    parameters.forEach(function(param) {
        if (param.in !== 'body') {
            if (!schema.properties[inMapping[param.in]]) {
                schema.properties[inMapping[param.in]] = {
                    type: 'object',
                    properties: {}
                };
                // 'required' array must have at least one element according to json-schema spec,
                // se we can't preinitialize it.
                schema.required = schema.required || [];
                if (schema.required.indexOf(inMapping[param.in]) < 0) {
                    schema.required.push(inMapping[param.in]);
                }
            }

            var reqPartSchema = schema.properties[inMapping[param.in]];
            var paramSchema = { type: param.type };
            supportedValidators.forEach(function(validator) {
                paramSchema[validator] = param[validator];
            });
            reqPartSchema.properties[param.name] = paramSchema;
            if (param.required) {
                reqPartSchema.required = reqPartSchema.required || [];
                reqPartSchema.required.push(param.name);
            }
        } else {
            if (param.schema) {
                schema.properties.body = param.schema;
            } else {
                schema.properties.body = {
                    type: 'object'
                };
            }
            if (param.required) {
                schema.required = schema.required || [];
                schema.required.push('body');
            }
        }
    });

    return schema;
};

/**
 * Creates a function, that tries to coerce types of parameters in the
 * incoming request, according to the provided parameters specification.
 *
 * @param parameters {Array} parameters swagger specification
 * @returns {Function(req, HTTPError)} cersion function
 * @private
 */
Validator.prototype._createTypeCoercionFunc = function(parameters) {
    var code = '';
    parameters.forEach(function(param) {
        if (param.type === 'string') {
            // Don't need to process strings
            return;
        }
        var paramAccessor = 'req.' + inMapping[param.in] + '["' + param.name + '"]';
        var paramCoercionCode = '';
        var errorNotifier;
        switch (param.type) {
            case 'integer':
                errorNotifier = 'throw new HTTPError({status:400,body:{type:"bad_request",'
                    + ' title:"Invalid parameters", detail: "data.'
                    + inMapping[param.in] + '.' + param.name + ' should be an integer"}});\n';
                paramCoercionCode += paramAccessor + ' = parseInt(' + paramAccessor + ');\n'
                    + 'if (!Number.isInteger(' + paramAccessor + ')) {\n' + errorNotifier + '}\n';
                break;
            case 'number':
                errorNotifier = 'throw new HTTPError({status:400,body:{type:"bad_request",'
                    + ' title:"Invalid parameters", detail: "data.'
                    + inMapping[param.in] + '.' + param.name + ' should be a number"}});\n';
                paramCoercionCode += paramAccessor + ' = parseFloat(' + paramAccessor + ');\n'
                    + 'if (Number.isNaN(' + paramAccessor + ')) {\n' + errorNotifier + '}\n';
                break;
            case 'boolean':
                errorNotifier = 'throw new HTTPError({status:400,body:{type:"bad_request",'
                    + ' title:"Invalid parameters", detail: "data.'
                    + inMapping[param.in] + '.' + param.name + ' should be a boolean.'
                    + ' true|false|1|0 is accepted as a boolean."}});\n';
                paramCoercionCode += 'if(!/^true|false|1|0$/i.test('
                    + paramAccessor + ' + "")) {\n'
                    + errorNotifier + '}\n'
                    + paramAccessor + ' = /^true|1$/i.test('
                    + paramAccessor + ' + "");\n';
        }

        if (paramCoercionCode) {
            var wrapperConditions = 'typeof ' + paramAccessor + " === 'string'";
            if (!param.required) {
                // If parameter is not required, don't try to coerce "undefined"
                wrapperConditions += ' && ' + paramAccessor + ' !== undefined';
            }
            paramCoercionCode = 'if (' + wrapperConditions + ') {\n'
                + paramCoercionCode + '}\n';
        }

        code += paramCoercionCode;
    });
    if (code && code.trim()) {
        code += '\nreturn req;\n';
        /*jshint evil:true */
        return new Function('req', 'HTTPError', code);
    } else {
        return undefined;
    }
};

/**
 * Validates a request. In case of an error, throws HTTPError with 400 code
 * @param req {Object} a request object to validate.
 */
Validator.prototype.validate = function(req) {
    if (this._paramCoercionFunc) {
        req = this._paramCoercionFunc(req, HTTPError);
    }
    if (this._bodyCoercionFunc
            && !/^ *application\/json/i.test(req.headers['content-type'])) {
        req = this._bodyCoercionFunc(req, HTTPError);
    }
    if (!this._validatorFunc(req)) {
        throw new HTTPError({
            status: 400,
            body: {
                type: 'bad_request',
                title: 'Invalid parameters',
                detail: this._ajv.errorsText(this._validatorFunc.errors),
                req: req
            }
        });
    }
};

module.exports = Validator;
<|endoftext|>


<commit_before>var assert = require('assert');
var buffer = require('buffer');
var crypto = require('crypto');
var events = require('events');
var http = require('http');
var net = require('net');
var urllib = require('url');
var sys = require('sys');

var FRAME_NO = 0;
var FRAME_LO = 1;
var FRAME_HI = 2;

// Values for readyState as per the W3C spec
var CONNECTING = 0;
var OPEN = 1;
var CLOSING = 2;
var CLOSED = 3;

var debugLevel = parseInt(process.env.NODE_DEBUG, 16);
var debug = (debugLevel & 0x4) ?
    function() { sys.error.apply(this, arguments); } :
    function() { };

// Generate a Sec-WebSocket-* value
var createSecretKey = function() {
    // How many spaces will we be inserting?
    var numSpaces = 1 + Math.floor(Math.random() * 12);
    assert.ok(1 <= numSpaces && numSpaces <= 12);

    // What is the numerical value of our key?
    var keyVal = (Math.floor(
        Math.random() * (4294967295 / numSpaces)
    ) * numSpaces);

    // Our string starts with a string representation of our key
    var s = keyVal.toString();

    // Insert 'numChars' worth of noise in the character ranges
    // [0x21, 0x2f] (14 characters) and [0x3a, 0x7e] (68 characters)
    var numChars = 1 + Math.floor(Math.random() * 12);
    assert.ok(1 <= numChars && numChars <= 12);

    for (var i = 0; i < numChars; i++) {
        var pos = Math.floor(Math.random() * s.length + 1);

        var c = Math.floor(Math.random() * (14 + 68));
        c = (c <= 14) ?
            String.fromCharCode(c + 0x21) :
            String.fromCharCode((c - 14) + 0x3a);

        s = s.substring(0, pos) + c + s.substring(pos, s.length);
    }

    // We shoudln't have any spaces in our value until we insert them
    assert.equal(s.indexOf(' '), -1);

    // Insert 'numSpaces' worth of spaces
    for (var i = 0; i < numSpaces; i++) {
        var pos = Math.floor(Math.random() * (s.length - 1)) + 1;
        s = s.substring(0, pos) + ' ' + s.substring(pos, s.length);
    }

    assert.notEqual(s.charAt(0), ' ');
    assert.notEqual(s.charAt(s.length), ' ');

    return s;
};

// Generate a challenge sequence
var createChallenge = function() {
    var c = '';
    for (var i = 0; i < 8; i++) {
        c += String.fromCharCode(Math.floor(Math.random() * 255));
    }

    return c;
};

// Get the value of a secret key string
//
// This strips non-digit values and divides the result by the number of
// spaces found.
var secretKeyValue = function(sk) {
    var ns = 0;
    var v = 0;

    for (var i = 0; i < sk.length; i++) {
        var cc = sk.charCodeAt(i);

        if (cc == 0x20) {
            ns++;
        } else if (0x30 <= cc && cc <= 0x39) {
            v = v * 10 + cc - 0x30;
        }
    }

    return Math.floor(v / ns);
}

// Get the to-be-hashed value of a secret key string
//
// This takes the result of secretKeyValue() and encodes it in a big-endian
// byte string
var secretKeyHashValue = function(sk) {
    var skv = secretKeyValue(sk);

    var hv = '';
    hv += String.fromCharCode((skv >> 24) & 0xff);
    hv += String.fromCharCode((skv >> 16) & 0xff);
    hv += String.fromCharCode((skv >> 8) & 0xff);
    hv += String.fromCharCode((skv >> 0) & 0xff);

    return hv;
};

// Compute the secret key signature based on two secret key strings and some
// handshaking data.
var computeSecretKeySignature = function(s1, s2, hs) {
    assert.equal(hs.length, 8);

    var hash = crypto.createHash('md5');

    hash.update(secretKeyHashValue(s1));
    hash.update(secretKeyHashValue(s2));
    hash.update(hs);

    return hash.digest('binary');
};

// Return a hex representation of the given binary string; used for debugging
var str2hex = function(str) {
    var hexChars = [
        '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
        'a', 'b', 'c', 'd', 'e', 'f'
    ];

    var out = '';
    for (var i = 0; i < str.length; i++) {
        var c = str.charCodeAt(i);
        out += hexChars[(c & 0xf0) >>> 4];
        out += hexChars[c & 0x0f];
        out += ' ';
    }

    return out.trim();
};

// Get the scheme for a URL, undefined if none is found
var getUrlScheme = function(url) {
    var i = url.indexOf(':');
    if (i == -1) {
        return undefined;
    }

    return url.substring(0, i);
};

// Set a constant on the given object
var setConstant = function(obj, name, value) {
    Object.defineProperty(obj, name, {
        get : function() {
            return value;
        }
    });
};

// WebSocket object
//
// This is intended to conform (mostly) to http://dev.w3.org/html5/websockets/
//
// N.B. Arguments are parsed in the anonymous function at the bottom of the
//      constructor.
var WebSocket = function(url, proto) {
    events.EventEmitter.call(this);

    // Retain a reference to our object
    var self = this;

    // State of the connection
    var readyState = CONNECTING;

    // Our underlying net.Stream instance
    var stream = undefined;

    // Frame parsing functions
    //
    // These read data from the given buffer starting at the given offset look
    // for the end of the current frame. If found, the current frame is emitted
    // and the function returns. Only a single frame is processed at a time.
    //
    // The number of bytes of completed frames read is returned, which the
    // caller is to use to advance along its buffer. If 0 is returned, no
    // completed frame bytes were found, and the caller should probably enqueue
    // the buffer as a continuation of the current message. If a complete frame
    // is read, the function is responsible fro resting 'frameType'.

    // Framing data
    var frameType = FRAME_NO;
    var bufs = [];
    var bufsBytes = 0;

    // Frame-parsing functions
    var frameFuncs = [
        // FRAME_NO
        function(buf, off) {
            if (buf[off] & 0x80) {
                throw new Error('High-byte frames not yet supported');
            }

            frameType = FRAME_LO;
            return 1;
        },

        // FRAME_LO
        function(buf, off) {
            assert.ok(bufs.length > 0 || bufsBytes == 0);

            debug('frame_lo(' + sys.inspect(buf) + ', ' + off + ')');

            // Find the first instance of 0xff, our terminating byte
            for (var i = off; i < buf.length && buf[i] != 0xff; i++)
                ;

            // We didn't find a terminating byte
            if (i >= buf.length) {
                return 0;
            }

            // We found a terminating byte; collect all bytes into a single buffer
            // and emit it
            var mb = null;
            if (bufs.length == 0) {
                mb = buf.slice(off, i);
            } else {
                mb = new buffer.Buffer(bufsBytes + i);

                var mbOff = 0;
                bufs.forEach(function(b) {
                    b.copy(mb, mbOff, 0, b.length);
                    mbOff += b.length;
                });

                assert.equal(mbOff, bufsBytes);

                buf.copy(mb, mbOff, off, i);
            }

            process.nextTick(function() {
                var b = mb;
                return function() {
                    self.emit('data', b);

                    if (self.onmessage) {
                        self.onmessage(b.toString('utf8'));
                    }
                };
            }());

            frameType = FRAME_NO;
            return i - off + 1;
        },

        // FRAME_HI
        function(buf, off) {
            debug('High-byte framing not yet supported');

            frameType = FRAME_NO;
            return buf.length - off;
        }
    ];

    // Handle data coming from our socket
    var dataListener = function(buf) {
        if (buf.length <= 0) {
            return;
        }

        debug('dataListener(' + sys.inspect(buf) + ')');

        var off = 0;
        var consumed = 0;

        do {
            if (frameType < 0 || frameFuncs.length <= frameType) {
                throw new Error('Unexpected frame type: ' + frameType);
            }

            consumed = frameFuncs[frameType](buf, off);
            off += consumed;
        } while (consumed > 0 && off < buf.length);

        if (consumed == 0) {
            bufs.push(buf.slice(off, buf.length));
            bufsBytes += buf.length - off;
        }
    };

    // External API
    self.close = function() {
        switch (readyState) {
        case CLOSED:
        case CLOSING:
            break;

        case CONNECTING:
            throw new Error(
                'Closing of an CONNECTING socket not yet implemented'
            );

        default:
            readyState = CLOSING;

            var f = function() {
                stream.end();
                stream.destroy();
                stream = undefined;
                readyState = CLOSED;

                process.nextTick(function() {
                    self.emit('close');

                    if (self.onclose) {
                        self.onclose();
                    }
                });
            };

            // XXX: Run f() on the next tick so that we conform a little
            //      closer to the spirit of the API in that the caller
            //      never sees us transition directly to CLOSED. Instead, we
            //      just seem to have an infinitely fast closing handshake.
            if (stream.write('', 'binary')) {
                process.nextTick(f);
            } else {
                stream.addListener('drain', f);
            }
        }
    };

    self.send = function(str, fd) {
        if (!stream) {
            throw new Error('Cannot write to closed WebSocket client');
        }

        stream.write('\x00', 'binary');
        stream.write(str, 'utf8', fd);
        stream.write('\xff', 'binary');
    };

    setConstant(self, 'url', url);

    Object.defineProperty(self, 'readyState',  {
        get : function() {
            return readyState;
        }
    });

    // Connect and perform handshaking with the server
    (function() {
        // Parse constructor arguments
        if (!url) {
            throw new Error('Url and must be specified.');
        }

        // Secrets used for handshaking
        var key1 = createSecretKey();
        var key2 = createSecretKey();
        var challenge = createChallenge();

        debug(
            'key1=\'' + str2hex(key1) + '\'; ' +
            'key2=\'' + str2hex(key2) + '\'; ' +
            'challenge=\'' + str2hex(challenge) + '\''
        );

        var httpHeaders = {
            'Connection' : 'Upgrade',
            'Upgrade' : 'WebSocket',
            'Sec-WebSocket-Key1' : key1,
            'Sec-WebSocket-Key2' : key2
        };
        var httpPath = '/';

        if (proto) {
            httpHeaders['Sec-WebSocket-Protocol'] = proto;
        }

        // Create the HTTP client that we'll use for handshaking. We'll cannabalize
        // its socket via the 'upgrade' event and leave it to rot.
        //
        // XXX: The ws+unix:// scheme makes use of the implementation detail
        //      that http.Client passes its constructor arguments through,
        //      un-inspected to net.Stream.connect(). The latter accepts a
        //      string as its first argument to connect to a UNIX socket.
        var httpClient = undefined;
        switch (getUrlScheme(url)) {
        case 'ws':
            var u = urllib.parse(url);
            httpClient = http.createClient(u.port || 80, u.hostname);
            httpPath = '/' + url.replace(/([^\/]*\/){3}/, '');
            httpHeaders.Host = u.hostname;
            break;

        case 'ws+unix':
            var sockPath = url.substring('ws+unix://'.length, url.length);
            httpClient = http.createClient(sockPath);
            httpHeaders.Host = 'localhost';
            break;

        default:
            throw new Error('Invalid URL scheme \'' + urlScheme + '\' specified.');
        }

        httpClient.addListener('upgrade', (function() {
            var data = undefined;

            return function(req, s, head) {
                stream = s;

                stream.addListener('data', function(d) {
                    if (!data) {
                        data = d;
                    } else {
                        var data2 = new buffer.Buffer(data.length + d.length);
                        data.copy(data2, 0, 0, data.length);
                        d.copy(data2, data.length, 0, d.length);

                        data = data2;
                    }

                    if (data.length >= 16) {
                        var expected = computeSecretKeySignature(key1, key2, challenge);
                        var actual = data.slice(0, 16).toString('binary');

                        // Handshaking fails; we're donezo
                        if (actual != expected) {
                            debug(
                                'expected=\'' + str2hex(expected) + '\'; ' +
                                'actual=\'' + str2hex(actual) + '\''
                            );

                            self.emit('error', new Error('Invalid handshake from server'));
                            self.close();
                        }

                        // Un-register our data handler and add the one to be used
                        // for the normal, non-handshaking case. If we have extra
                        // data left over, manually fire off the handler on
                        // whatever remains.
                        //
                        // XXX: This is lame. We should only remove the listeners
                        //      that we added.
                        httpClient.removeAllListeners('upgrade');
                        stream.removeAllListeners('data');
                        stream.addListener('data', dataListener);

                        // Fire the 'open' event
                        process.nextTick(function() {
                            self.emit('open');

                            if (self.onopen) {
                                self.onopen();
                            }
                        });

                        readyState = OPEN;

                        if (data.length > 16) {
                            stream.emit('data', data.slice(16, data.length));
                        }
                    }
                });

                stream.emit('data', head);
            };
        })());

        var httpReq = httpClient.request(httpPath, httpHeaders);

        httpReq.write(challenge, 'binary');
        httpReq.end();
    })();
};
sys.inherits(WebSocket, events.EventEmitter);
exports.WebSocket = WebSocket;

// Add some constants to the WebSocket object
setConstant(WebSocket.prototype, 'CONNECTING', CONNECTING);
setConstant(WebSocket.prototype, 'OPEN', OPEN);
setConstant(WebSocket.prototype, 'CLOSING', CLOSING);
setConstant(WebSocket.prototype, 'CLOSED', CLOSED);

// vim:ts=4 sw=4 et
<commit_msg>Disallow send() on sockets which are not OPEN.<commit_after>var assert = require('assert');
var buffer = require('buffer');
var crypto = require('crypto');
var events = require('events');
var http = require('http');
var net = require('net');
var urllib = require('url');
var sys = require('sys');

var FRAME_NO = 0;
var FRAME_LO = 1;
var FRAME_HI = 2;

// Values for readyState as per the W3C spec
var CONNECTING = 0;
var OPEN = 1;
var CLOSING = 2;
var CLOSED = 3;

var debugLevel = parseInt(process.env.NODE_DEBUG, 16);
var debug = (debugLevel & 0x4) ?
    function() { sys.error.apply(this, arguments); } :
    function() { };

// Generate a Sec-WebSocket-* value
var createSecretKey = function() {
    // How many spaces will we be inserting?
    var numSpaces = 1 + Math.floor(Math.random() * 12);
    assert.ok(1 <= numSpaces && numSpaces <= 12);

    // What is the numerical value of our key?
    var keyVal = (Math.floor(
        Math.random() * (4294967295 / numSpaces)
    ) * numSpaces);

    // Our string starts with a string representation of our key
    var s = keyVal.toString();

    // Insert 'numChars' worth of noise in the character ranges
    // [0x21, 0x2f] (14 characters) and [0x3a, 0x7e] (68 characters)
    var numChars = 1 + Math.floor(Math.random() * 12);
    assert.ok(1 <= numChars && numChars <= 12);

    for (var i = 0; i < numChars; i++) {
        var pos = Math.floor(Math.random() * s.length + 1);

        var c = Math.floor(Math.random() * (14 + 68));
        c = (c <= 14) ?
            String.fromCharCode(c + 0x21) :
            String.fromCharCode((c - 14) + 0x3a);

        s = s.substring(0, pos) + c + s.substring(pos, s.length);
    }

    // We shoudln't have any spaces in our value until we insert them
    assert.equal(s.indexOf(' '), -1);

    // Insert 'numSpaces' worth of spaces
    for (var i = 0; i < numSpaces; i++) {
        var pos = Math.floor(Math.random() * (s.length - 1)) + 1;
        s = s.substring(0, pos) + ' ' + s.substring(pos, s.length);
    }

    assert.notEqual(s.charAt(0), ' ');
    assert.notEqual(s.charAt(s.length), ' ');

    return s;
};

// Generate a challenge sequence
var createChallenge = function() {
    var c = '';
    for (var i = 0; i < 8; i++) {
        c += String.fromCharCode(Math.floor(Math.random() * 255));
    }

    return c;
};

// Get the value of a secret key string
//
// This strips non-digit values and divides the result by the number of
// spaces found.
var secretKeyValue = function(sk) {
    var ns = 0;
    var v = 0;

    for (var i = 0; i < sk.length; i++) {
        var cc = sk.charCodeAt(i);

        if (cc == 0x20) {
            ns++;
        } else if (0x30 <= cc && cc <= 0x39) {
            v = v * 10 + cc - 0x30;
        }
    }

    return Math.floor(v / ns);
}

// Get the to-be-hashed value of a secret key string
//
// This takes the result of secretKeyValue() and encodes it in a big-endian
// byte string
var secretKeyHashValue = function(sk) {
    var skv = secretKeyValue(sk);

    var hv = '';
    hv += String.fromCharCode((skv >> 24) & 0xff);
    hv += String.fromCharCode((skv >> 16) & 0xff);
    hv += String.fromCharCode((skv >> 8) & 0xff);
    hv += String.fromCharCode((skv >> 0) & 0xff);

    return hv;
};

// Compute the secret key signature based on two secret key strings and some
// handshaking data.
var computeSecretKeySignature = function(s1, s2, hs) {
    assert.equal(hs.length, 8);

    var hash = crypto.createHash('md5');

    hash.update(secretKeyHashValue(s1));
    hash.update(secretKeyHashValue(s2));
    hash.update(hs);

    return hash.digest('binary');
};

// Return a hex representation of the given binary string; used for debugging
var str2hex = function(str) {
    var hexChars = [
        '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
        'a', 'b', 'c', 'd', 'e', 'f'
    ];

    var out = '';
    for (var i = 0; i < str.length; i++) {
        var c = str.charCodeAt(i);
        out += hexChars[(c & 0xf0) >>> 4];
        out += hexChars[c & 0x0f];
        out += ' ';
    }

    return out.trim();
};

// Get the scheme for a URL, undefined if none is found
var getUrlScheme = function(url) {
    var i = url.indexOf(':');
    if (i == -1) {
        return undefined;
    }

    return url.substring(0, i);
};

// Set a constant on the given object
var setConstant = function(obj, name, value) {
    Object.defineProperty(obj, name, {
        get : function() {
            return value;
        }
    });
};

// WebSocket object
//
// This is intended to conform (mostly) to http://dev.w3.org/html5/websockets/
//
// N.B. Arguments are parsed in the anonymous function at the bottom of the
//      constructor.
var WebSocket = function(url, proto) {
    events.EventEmitter.call(this);

    // Retain a reference to our object
    var self = this;

    // State of the connection
    var readyState = CONNECTING;

    // Our underlying net.Stream instance
    var stream = undefined;

    // Frame parsing functions
    //
    // These read data from the given buffer starting at the given offset look
    // for the end of the current frame. If found, the current frame is emitted
    // and the function returns. Only a single frame is processed at a time.
    //
    // The number of bytes of completed frames read is returned, which the
    // caller is to use to advance along its buffer. If 0 is returned, no
    // completed frame bytes were found, and the caller should probably enqueue
    // the buffer as a continuation of the current message. If a complete frame
    // is read, the function is responsible fro resting 'frameType'.

    // Framing data
    var frameType = FRAME_NO;
    var bufs = [];
    var bufsBytes = 0;

    // Frame-parsing functions
    var frameFuncs = [
        // FRAME_NO
        function(buf, off) {
            if (buf[off] & 0x80) {
                throw new Error('High-byte frames not yet supported');
            }

            frameType = FRAME_LO;
            return 1;
        },

        // FRAME_LO
        function(buf, off) {
            assert.ok(bufs.length > 0 || bufsBytes == 0);

            debug('frame_lo(' + sys.inspect(buf) + ', ' + off + ')');

            // Find the first instance of 0xff, our terminating byte
            for (var i = off; i < buf.length && buf[i] != 0xff; i++)
                ;

            // We didn't find a terminating byte
            if (i >= buf.length) {
                return 0;
            }

            // We found a terminating byte; collect all bytes into a single buffer
            // and emit it
            var mb = null;
            if (bufs.length == 0) {
                mb = buf.slice(off, i);
            } else {
                mb = new buffer.Buffer(bufsBytes + i);

                var mbOff = 0;
                bufs.forEach(function(b) {
                    b.copy(mb, mbOff, 0, b.length);
                    mbOff += b.length;
                });

                assert.equal(mbOff, bufsBytes);

                buf.copy(mb, mbOff, off, i);
            }

            process.nextTick(function() {
                var b = mb;
                return function() {
                    self.emit('data', b);

                    if (self.onmessage) {
                        self.onmessage(b.toString('utf8'));
                    }
                };
            }());

            frameType = FRAME_NO;
            return i - off + 1;
        },

        // FRAME_HI
        function(buf, off) {
            debug('High-byte framing not yet supported');

            frameType = FRAME_NO;
            return buf.length - off;
        }
    ];

    // Handle data coming from our socket
    var dataListener = function(buf) {
        if (buf.length <= 0) {
            return;
        }

        debug('dataListener(' + sys.inspect(buf) + ')');

        var off = 0;
        var consumed = 0;

        do {
            if (frameType < 0 || frameFuncs.length <= frameType) {
                throw new Error('Unexpected frame type: ' + frameType);
            }

            consumed = frameFuncs[frameType](buf, off);
            off += consumed;
        } while (consumed > 0 && off < buf.length);

        if (consumed == 0) {
            bufs.push(buf.slice(off, buf.length));
            bufsBytes += buf.length - off;
        }
    };

    // External API
    self.close = function() {
        switch (readyState) {
        case CLOSED:
        case CLOSING:
            break;

        case CONNECTING:
            throw new Error(
                'Closing of an CONNECTING socket not yet implemented'
            );

        default:
            readyState = CLOSING;

            var f = function() {
                stream.end();
                stream.destroy();
                stream = undefined;
                readyState = CLOSED;

                process.nextTick(function() {
                    self.emit('close');

                    if (self.onclose) {
                        self.onclose();
                    }
                });
            };

            // XXX: Run f() on the next tick so that we conform a little
            //      closer to the spirit of the API in that the caller
            //      never sees us transition directly to CLOSED. Instead, we
            //      just seem to have an infinitely fast closing handshake.
            if (stream.write('', 'binary')) {
                process.nextTick(f);
            } else {
                stream.addListener('drain', f);
            }
        }
    };

    self.send = function(str, fd) {
        if (readyState != OPEN) {
            throw new Error('Cannot write to non-open WebSocket client');
        }

        stream.write('\x00', 'binary');
        stream.write(str, 'utf8', fd);
        stream.write('\xff', 'binary');
    };

    setConstant(self, 'url', url);

    Object.defineProperty(self, 'readyState',  {
        get : function() {
            return readyState;
        }
    });

    // Connect and perform handshaking with the server
    (function() {
        // Parse constructor arguments
        if (!url) {
            throw new Error('Url and must be specified.');
        }

        // Secrets used for handshaking
        var key1 = createSecretKey();
        var key2 = createSecretKey();
        var challenge = createChallenge();

        debug(
            'key1=\'' + str2hex(key1) + '\'; ' +
            'key2=\'' + str2hex(key2) + '\'; ' +
            'challenge=\'' + str2hex(challenge) + '\''
        );

        var httpHeaders = {
            'Connection' : 'Upgrade',
            'Upgrade' : 'WebSocket',
            'Sec-WebSocket-Key1' : key1,
            'Sec-WebSocket-Key2' : key2
        };
        var httpPath = '/';

        if (proto) {
            httpHeaders['Sec-WebSocket-Protocol'] = proto;
        }

        // Create the HTTP client that we'll use for handshaking. We'll cannabalize
        // its socket via the 'upgrade' event and leave it to rot.
        //
        // XXX: The ws+unix:// scheme makes use of the implementation detail
        //      that http.Client passes its constructor arguments through,
        //      un-inspected to net.Stream.connect(). The latter accepts a
        //      string as its first argument to connect to a UNIX socket.
        var httpClient = undefined;
        switch (getUrlScheme(url)) {
        case 'ws':
            var u = urllib.parse(url);
            httpClient = http.createClient(u.port || 80, u.hostname);
            httpPath = '/' + url.replace(/([^\/]*\/){3}/, '');
            httpHeaders.Host = u.hostname;
            break;

        case 'ws+unix':
            var sockPath = url.substring('ws+unix://'.length, url.length);
            httpClient = http.createClient(sockPath);
            httpHeaders.Host = 'localhost';
            break;

        default:
            throw new Error('Invalid URL scheme \'' + urlScheme + '\' specified.');
        }

        httpClient.addListener('upgrade', (function() {
            var data = undefined;

            return function(req, s, head) {
                stream = s;

                stream.addListener('data', function(d) {
                    if (!data) {
                        data = d;
                    } else {
                        var data2 = new buffer.Buffer(data.length + d.length);
                        data.copy(data2, 0, 0, data.length);
                        d.copy(data2, data.length, 0, d.length);

                        data = data2;
                    }

                    if (data.length >= 16) {
                        var expected = computeSecretKeySignature(key1, key2, challenge);
                        var actual = data.slice(0, 16).toString('binary');

                        // Handshaking fails; we're donezo
                        if (actual != expected) {
                            debug(
                                'expected=\'' + str2hex(expected) + '\'; ' +
                                'actual=\'' + str2hex(actual) + '\''
                            );

                            self.emit('error', new Error('Invalid handshake from server'));
                            self.close();
                        }

                        // Un-register our data handler and add the one to be used
                        // for the normal, non-handshaking case. If we have extra
                        // data left over, manually fire off the handler on
                        // whatever remains.
                        //
                        // XXX: This is lame. We should only remove the listeners
                        //      that we added.
                        httpClient.removeAllListeners('upgrade');
                        stream.removeAllListeners('data');
                        stream.addListener('data', dataListener);

                        // Fire the 'open' event
                        process.nextTick(function() {
                            self.emit('open');

                            if (self.onopen) {
                                self.onopen();
                            }
                        });

                        readyState = OPEN;

                        if (data.length > 16) {
                            stream.emit('data', data.slice(16, data.length));
                        }
                    }
                });

                stream.emit('data', head);
            };
        })());

        var httpReq = httpClient.request(httpPath, httpHeaders);

        httpReq.write(challenge, 'binary');
        httpReq.end();
    })();
};
sys.inherits(WebSocket, events.EventEmitter);
exports.WebSocket = WebSocket;

// Add some constants to the WebSocket object
setConstant(WebSocket.prototype, 'CONNECTING', CONNECTING);
setConstant(WebSocket.prototype, 'OPEN', OPEN);
setConstant(WebSocket.prototype, 'CLOSING', CLOSING);
setConstant(WebSocket.prototype, 'CLOSED', CLOSED);

// vim:ts=4 sw=4 et
<|endoftext|>


<commit_before>var _ = require('lodash');
var async = require('async-chainable');
var asyncExec = require('async-chainable-exec');
var os = require('os');
var which = require('which');

module.exports = {
	settings: {
		topProcessCount: 5,
	},
	poll: function(finish) {
		var settings = this.settings;

		async()
			.use(asyncExec)
			.set('totalRead', undefined)
			.set('totalWrite', undefined)
			.set('topIO', undefined)
			.exec('iotop', [
				'sudo',
				'iotop',
				'-b',
				'-n1',
				'-o',
				'-P',
				'-k',
			])
			.then(function(next) {
				var self = this;
				var iotopSlicer = /^\s*([0-9]+)\s+(.+?)\s+(.+?)\s+([0-9\.]+) K\/s\s+([0-9\.]+) K\/s\s+([0-9\.]+) %\s+([0-9\.]+) %\s+(.*)$/;
				self.topIO = _(this.iotop)
					.map(function(line) { return line.split('\n') })
					.tap(function(lines) {
						// Process first line of output which gives us the total system I/O {{{
						var bits = /Total DISK READ\s+:\s+([0-9\.]+) K\/s\s+\|\s+Total DISK WRITE\s+:\s+([0-9\.]+) K\/s/.exec(lines[0]);
						if (bits) {
							self.totalRead = bits[1];
							self.totalWrite = bits[2];
						}
						// }}}
					})
					.flatten()
					.slice(3, 3 + settings.topProcessCount)
					.map(function(line) {
						var bits = iotopSlicer.exec(line);
						if (!bits) return null;
						return {
							pid: bits[1],
							// priority: bits[2],
							// user: bits[3],
							ioRead: bits[4],
							ioWrite: bits[5],
							// swapPercent: bits[6],
							// ioPercent: bits[7],
							name: bits[8],
						};
					})
					.value();
				next();
			})
			.end(function(err) {
				if (err) return finish(err);
				finish(null, {
					io: {
						totalRead: this.totalRead,
						totalWrite: this.totalWrite,
					},
					topIO: this.topIO,
				});
			});
	},
	register: function(finish) {
		which('iotop', function(err, path) {
			if (err) return finish('Needed stats binary `iotop` is not in PATH. Either install it or disable the `io` module');

			finish();
		});
	},
	unregister: function(finish) {
		finish();
	},
};
<commit_msg>BUGFIX: Invalid lines from `iotop` are now omitted BUGFIX: Removed redundent unregister function from io module<commit_after>var _ = require('lodash');
var async = require('async-chainable');
var asyncExec = require('async-chainable-exec');
var os = require('os');
var which = require('which');

module.exports = {
	settings: {
		topProcessCount: 5,
	},
	poll: function(finish) {
		var settings = this.settings;

		async()
			.use(asyncExec)
			.set('totalRead', undefined)
			.set('totalWrite', undefined)
			.set('topIO', undefined)
			.exec('iotop', [
				'sudo',
				'iotop',
				'-b',
				'-n1',
				'-o',
				'-P',
				'-k',
			])
			.then(function(next) {
				var self = this;
				var iotopSlicer = /^\s*([0-9]+)\s+(.+?)\s+(.+?)\s+([0-9\.]+) K\/s\s+([0-9\.]+) K\/s\s+([0-9\.]+) %\s+([0-9\.]+) %\s+(.*)$/;
				self.topIO = _(this.iotop)
					.map(function(line) { return line.split('\n') })
					.tap(function(lines) {
						// Process first line of output which gives us the total system I/O {{{
						var bits = /Total DISK READ\s+:\s+([0-9\.]+) K\/s\s+\|\s+Total DISK WRITE\s+:\s+([0-9\.]+) K\/s/.exec(lines[0]);
						if (bits) {
							self.totalRead = bits[1];
							self.totalWrite = bits[2];
						}
						// }}}
					})
					.flatten()
					.slice(3, 3 + settings.topProcessCount)
					.map(function(line) {
						var bits = iotopSlicer.exec(line);
						if (!bits) return null;
						return {
							pid: bits[1],
							// priority: bits[2],
							// user: bits[3],
							ioRead: bits[4],
							ioWrite: bits[5],
							// swapPercent: bits[6],
							// ioPercent: bits[7],
							name: bits[8],
						};
					})
					.filter()
					.value();
				next();
			})
			.end(function(err) {
				if (err) return finish(err);
				finish(null, {
					io: {
						totalRead: this.totalRead,
						totalWrite: this.totalWrite,
					},
					topIO: this.topIO,
				});
			});
	},
	register: function(finish) {
		which('iotop', function(err, path) {
			if (err) return finish('Needed stats binary `iotop` is not in PATH. Either install it or disable the `io` module');

			finish();
		});
	},
};
<|endoftext|>


<commit_before>

var mapValues = require("./mapValues")
var matches = require("./matches")

var DotNotationPointers = exports.DotNotationPointers = require("./DotNotationPointers")

// routerDefinition should be a function that gets a Route object as its `this` context
var Parse = function(mongoQuery) {
    this.parts = parseQuery(mongoQuery)
}
Parse.prototype = {}

// instance methods
Parse.prototype.map = function(callback) {
    return map(this.parts, callback)
}
Parse.prototype.mapValues = function(callback) {
    return compressQuery(mapValues(this.parts, callback))
}
Parse.prototype.matches = function(document, validate) {
    return matches(this.parts, document, validate)
}

exports.parse = function(mongoQuery) {
    return new Parse(mongoQuery)
}
exports.inclusive = function(mongoProjection) {
    return isInclusive(mongoProjection)
}

exports.search = function(documents, query, sort, validate) {
    var parsedQuery = new Parse(query)

    return documents.filter(function(doc) {
        return parsedQuery.matches(doc, validate)
    }).sort(function(a,b) {
        for(var k in sort) {
            var result = sortCompare(a,b,k)
            if(result !== 0) {
                if(sort[k]<0)
                    result = -result

                return result
            }
        }

        return 0 // if it got here, they're the same
    })
}

var complexFieldIndependantOperators = {$and:1, $or:1, $nor:1}
var simpleFieldIndependantOperators = {$text:1, $comment:1}

// compares two documents by a single sort property
function sortCompare(a,b,sortProperty) {
    var aVal = DotNotationPointers(a, sortProperty)[0].val // todo: figure out what mongo does with multiple matching sort properties
    var bVal = DotNotationPointers(b, sortProperty)[0].val

    if(aVal > bVal) {
        return 1
    } else if(aVal < bVal) {
        return -1
    } else {
        return 0
    }
}

function isInclusive(projection) {
    for(var k in projection) {
        if(!projection[k]) {
            if(k !== '_id') {
                return false
            }
        } else if(k === '$meta') {
            return true
        } else if(projection[k]) {
            if(projection[k] instanceof Object && ('$elemMatch' in projection[k] || '$slice'  in projection[k])) {
                // ignore
            } else {
                return true
            }
        }
    }
}

function parseQuery(query) {
    if(query instanceof Function || typeof(query) === 'string') {
        if(query instanceof Function) {
            query = "("+query+").call(obj)"
        }

        var normalizedFunction = new Function("return function(){var obj=this; return "+query+"}")()
        return [new Part(undefined, '$where', normalizedFunction)]
    }
    // else

    var parts = []
    for(var key in query) {
        if(key in complexFieldIndependantOperators) { // a field-independant operator
            var operator = key
            var operands = query[key]
            var innerParts = []
            operands.forEach(function(operand) {
                innerParts.push(new Part(undefined, '$and', [operand], parseQuery(operand)))
            })

            parts.push(new Part(undefined, operator, query[key], innerParts))
        } else if(key in simpleFieldIndependantOperators) {
            parts.push(new Part(undefined, key, query[key]))
        } else { // a field
            var field = key
            if(isObject(query[key]) && fieldOperand(query[key])) {
                for(var innerOperator in query[key]) {
                    var innerOperand = query[key][innerOperator]
                    parts.push(parseFieldOperator(field, innerOperator, innerOperand))
                }
            } else { // just a value, shorthand for $eq
                parts.push(new Part(field, '$eq', query[key]))
            }
        }
    }

    return parts
}

function map(parts, callback) {
    var result = {}
    parts.forEach(function(part) {
        if(part.operator === '$and') {
            var mappedResult = map(part.parts, callback)
        } else if(part.operator in complexFieldIndependantOperators) {
            var mappedParts = part.parts.map(function(part) {
                return map(part.parts, callback)
            })
            var mappedResult = {$or: mappedParts}
        } else {
            var value = {}; value[part.operator] = part.operand
            var cbResult = callback(part.field, value)
            var mappedResult = processMappedResult(part, cbResult)
        }

        mergeQueries(result, mappedResult)
    })

    compressQuery(result)
    return result

    function processMappedResult(part, mappedResult) {
        if(mappedResult === undefined) {
            var result = {}
            if(part.field === undefined) {
                result[part.operator] = part.operand
            } else {
                var operation = {}
                operation[part.operator] = part.operand
                result[part.field] = operation
            }

            return result
        } else if(mappedResult ===  null) {
            return {}
        } else {
            return mappedResult
        }
    }
}

// merges query b into query a, resolving conflicts by using $and (or other techniques)
function mergeQueries(a,b) {
    for(var k in b) {
        if(k in a) {
            if(k === '$and') {
                a[k] = a[k].concat(b[k])
            } else {
                var andOperandA = {}; andOperandA[k] = a[k]
                var andOperandB = {}; andOperandB[k] = b[k]
                var and = {$and:[andOperandA,andOperandB]}
                delete a[k]
                mergeQueries(a,and)
            }
        } else {
            a[k] = b[k]
        }
    }
}

// decanonicalizes the query to remove any $and or $eq that can be merged up with its parent object
// compresses in place (mutates)
var compressQuery = exports.compressQuery = function (x) {
    for(var operator in complexFieldIndependantOperators) {
        if(operator in x) {
            x[operator].forEach(function(query){
                compressQuery(query)
            })
        }
    }
    if('$and' in x) {
        x.$and.forEach(function(andOperand) {
            for(var k in andOperand) {
                if(k in x) {
                    if(!(x[k] instanceof Array) && typeof(x[k]) === 'object' && k[0] !== '$') {
                        for(var operator in andOperand[k]) {
                            if(!(operator in x[k])) {
                                x[k][operator] = andOperand[k][operator]
                                delete andOperand[k][operator]
                                if(Object.keys(andOperand[k]).length === 0)
                                    delete andOperand[k]
                            }
                        }
                    }
                } else {
                    x[k] = andOperand[k]
                    delete andOperand[k]
                }
            }
        })
        x.$and = filterEmpties(x.$and)
        if(x.$and.length === 0) {
            delete x.$and
        }
    }
    if('$or' in x) {
        x.$or = filterEmpties(x.$or)
        if(x.$or.length === 0) {
            delete x.$or
        } else if(x.$or.length === 1) {
            var orOperand = x.$or[0]
            delete x.$or
            mergeQueries(x,orOperand)
        }
    }

    for(var k in x) {
        if(x[k].$eq !== undefined && Object.keys(x[k]).length === 1) {
            x[k] = x[k].$eq
        }
        if(x[k] && x[k].$elemMatch !== undefined) {
            compressQuery(x[k].$elemMatch)
        }
    }

    return x

    function filterEmpties(a) {
        return a.filter(function(operand) {
            if(Object.keys(operand).length === 0)
                return false
            else
                return true
        })
    }
}

// returns a Part object
function parseFieldOperator(field, operator, operand) {
    if(operator === '$elemMatch') {
        var elemMatchInfo = parseElemMatch(operand)
        var innerParts = elemMatchInfo.parts
        var implicitField = elemMatchInfo.implicitField
    } else if(operator === '$not') {
        var innerParts = parseNot(field, operand)
    } else {
        var innerParts = []
    }
    return new Part(field, operator, operand, innerParts, implicitField)
}

// takes in the operand of the $elemMatch operator
// returns the parts that operand parses to, and the implicitField value
function parseElemMatch(operand) {
    if(fieldOperand(operand)) {
        var parts = []
        for(var operator in operand) {
            var innerOperand = operand[operator]
            parts.push(parseFieldOperator(undefined, operator, innerOperand))
        }
        return {parts: parts, implicitField: true}
    } else {          // non-field operators ( stuff like {a:5} or {$and:[...]} )
        return {parts: parseQuery(operand), implicitField: false}
    }
}

function parseNot(field, operand) {
    var parts = []
    for(var operator in operand) {
        var subOperand = operand[operator]
        parts.push(parseFieldOperator(field, operator, subOperand))
    }
    return parts
}

// returns true for objects like {$gt:5}, {$elemMatch:{...}}
// returns false for objects like {x:4} and {$or:[...]}
function fieldOperand(obj) {
    for(var key in obj) {
        return key[0] === '$' && !(key in complexFieldIndependantOperators) // yes i know this won't actually loop
    }
}

// returns true if the value is an object and *not* an array
function isObject(value) {
    return value instanceof Object && !(value instanceof Array)
}

var Part = function(field, operator, operand, parts, implicitField) {
    if(parts === undefined) parts = []

    this.field = field
    this.operator = operator
    this.operand = operand
    this.parts = parts
    this.implicitField = implicitField // only used for a certain type of $elemMatch part
}
<commit_msg>Support null values for already compressed queries<commit_after>

var mapValues = require("./mapValues")
var matches = require("./matches")

var DotNotationPointers = exports.DotNotationPointers = require("./DotNotationPointers")

// routerDefinition should be a function that gets a Route object as its `this` context
var Parse = function(mongoQuery) {
    this.parts = parseQuery(mongoQuery)
}
Parse.prototype = {}

// instance methods
Parse.prototype.map = function(callback) {
    return map(this.parts, callback)
}
Parse.prototype.mapValues = function(callback) {
    return compressQuery(mapValues(this.parts, callback))
}
Parse.prototype.matches = function(document, validate) {
    return matches(this.parts, document, validate)
}

exports.parse = function(mongoQuery) {
    return new Parse(mongoQuery)
}
exports.inclusive = function(mongoProjection) {
    return isInclusive(mongoProjection)
}

exports.search = function(documents, query, sort, validate) {
    var parsedQuery = new Parse(query)

    return documents.filter(function(doc) {
        return parsedQuery.matches(doc, validate)
    }).sort(function(a,b) {
        for(var k in sort) {
            var result = sortCompare(a,b,k)
            if(result !== 0) {
                if(sort[k]<0)
                    result = -result

                return result
            }
        }

        return 0 // if it got here, they're the same
    })
}

var complexFieldIndependantOperators = {$and:1, $or:1, $nor:1}
var simpleFieldIndependantOperators = {$text:1, $comment:1}

// compares two documents by a single sort property
function sortCompare(a,b,sortProperty) {
    var aVal = DotNotationPointers(a, sortProperty)[0].val // todo: figure out what mongo does with multiple matching sort properties
    var bVal = DotNotationPointers(b, sortProperty)[0].val

    if(aVal > bVal) {
        return 1
    } else if(aVal < bVal) {
        return -1
    } else {
        return 0
    }
}

function isInclusive(projection) {
    for(var k in projection) {
        if(!projection[k]) {
            if(k !== '_id') {
                return false
            }
        } else if(k === '$meta') {
            return true
        } else if(projection[k]) {
            if(projection[k] instanceof Object && ('$elemMatch' in projection[k] || '$slice'  in projection[k])) {
                // ignore
            } else {
                return true
            }
        }
    }
}

function parseQuery(query) {
    if(query instanceof Function || typeof(query) === 'string') {
        if(query instanceof Function) {
            query = "("+query+").call(obj)"
        }

        var normalizedFunction = new Function("return function(){var obj=this; return "+query+"}")()
        return [new Part(undefined, '$where', normalizedFunction)]
    }
    // else

    var parts = []
    for(var key in query) {
        if(key in complexFieldIndependantOperators) { // a field-independant operator
            var operator = key
            var operands = query[key]
            var innerParts = []
            operands.forEach(function(operand) {
                innerParts.push(new Part(undefined, '$and', [operand], parseQuery(operand)))
            })

            parts.push(new Part(undefined, operator, query[key], innerParts))
        } else if(key in simpleFieldIndependantOperators) {
            parts.push(new Part(undefined, key, query[key]))
        } else { // a field
            var field = key
            if(isObject(query[key]) && fieldOperand(query[key])) {
                for(var innerOperator in query[key]) {
                    var innerOperand = query[key][innerOperator]
                    parts.push(parseFieldOperator(field, innerOperator, innerOperand))
                }
            } else { // just a value, shorthand for $eq
                parts.push(new Part(field, '$eq', query[key]))
            }
        }
    }

    return parts
}

function map(parts, callback) {
    var result = {}
    parts.forEach(function(part) {
        if(part.operator === '$and') {
            var mappedResult = map(part.parts, callback)
        } else if(part.operator in complexFieldIndependantOperators) {
            var mappedParts = part.parts.map(function(part) {
                return map(part.parts, callback)
            })
            var mappedResult = {$or: mappedParts}
        } else {
            var value = {}; value[part.operator] = part.operand
            var cbResult = callback(part.field, value)
            var mappedResult = processMappedResult(part, cbResult)
        }

        mergeQueries(result, mappedResult)
    })

    compressQuery(result)
    return result

    function processMappedResult(part, mappedResult) {
        if(mappedResult === undefined) {
            var result = {}
            if(part.field === undefined) {
                result[part.operator] = part.operand
            } else {
                var operation = {}
                operation[part.operator] = part.operand
                result[part.field] = operation
            }

            return result
        } else if(mappedResult ===  null) {
            return {}
        } else {
            return mappedResult
        }
    }
}

// merges query b into query a, resolving conflicts by using $and (or other techniques)
function mergeQueries(a,b) {
    for(var k in b) {
        if(k in a) {
            if(k === '$and') {
                a[k] = a[k].concat(b[k])
            } else {
                var andOperandA = {}; andOperandA[k] = a[k]
                var andOperandB = {}; andOperandB[k] = b[k]
                var and = {$and:[andOperandA,andOperandB]}
                delete a[k]
                mergeQueries(a,and)
            }
        } else {
            a[k] = b[k]
        }
    }
}

// decanonicalizes the query to remove any $and or $eq that can be merged up with its parent object
// compresses in place (mutates)
var compressQuery = exports.compressQuery = function (x) {
    for(var operator in complexFieldIndependantOperators) {
        if(operator in x) {
            x[operator].forEach(function(query){
                compressQuery(query)
            })
        }
    }
    if('$and' in x) {
        x.$and.forEach(function(andOperand) {
            for(var k in andOperand) {
                if(k in x) {
                    if(!(x[k] instanceof Array) && typeof(x[k]) === 'object' && k[0] !== '$') {
                        for(var operator in andOperand[k]) {
                            if(!(operator in x[k])) {
                                x[k][operator] = andOperand[k][operator]
                                delete andOperand[k][operator]
                                if(Object.keys(andOperand[k]).length === 0)
                                    delete andOperand[k]
                            }
                        }
                    }
                } else {
                    x[k] = andOperand[k]
                    delete andOperand[k]
                }
            }
        })
        x.$and = filterEmpties(x.$and)
        if(x.$and.length === 0) {
            delete x.$and
        }
    }
    if('$or' in x) {
        x.$or = filterEmpties(x.$or)
        if(x.$or.length === 0) {
            delete x.$or
        } else if(x.$or.length === 1) {
            var orOperand = x.$or[0]
            delete x.$or
            mergeQueries(x,orOperand)
        }
    }

    for(var k in x) {
        if(x[k] && x[k].$eq !== undefined && Object.keys(x[k]).length === 1) {
            x[k] = x[k].$eq
        }
        if(x[k] && x[k].$elemMatch !== undefined) {
            compressQuery(x[k].$elemMatch)
        }
    }

    return x

    function filterEmpties(a) {
        return a.filter(function(operand) {
            if(Object.keys(operand).length === 0)
                return false
            else
                return true
        })
    }
}

// returns a Part object
function parseFieldOperator(field, operator, operand) {
    if(operator === '$elemMatch') {
        var elemMatchInfo = parseElemMatch(operand)
        var innerParts = elemMatchInfo.parts
        var implicitField = elemMatchInfo.implicitField
    } else if(operator === '$not') {
        var innerParts = parseNot(field, operand)
    } else {
        var innerParts = []
    }
    return new Part(field, operator, operand, innerParts, implicitField)
}

// takes in the operand of the $elemMatch operator
// returns the parts that operand parses to, and the implicitField value
function parseElemMatch(operand) {
    if(fieldOperand(operand)) {
        var parts = []
        for(var operator in operand) {
            var innerOperand = operand[operator]
            parts.push(parseFieldOperator(undefined, operator, innerOperand))
        }
        return {parts: parts, implicitField: true}
    } else {          // non-field operators ( stuff like {a:5} or {$and:[...]} )
        return {parts: parseQuery(operand), implicitField: false}
    }
}

function parseNot(field, operand) {
    var parts = []
    for(var operator in operand) {
        var subOperand = operand[operator]
        parts.push(parseFieldOperator(field, operator, subOperand))
    }
    return parts
}

// returns true for objects like {$gt:5}, {$elemMatch:{...}}
// returns false for objects like {x:4} and {$or:[...]}
function fieldOperand(obj) {
    for(var key in obj) {
        return key[0] === '$' && !(key in complexFieldIndependantOperators) // yes i know this won't actually loop
    }
}

// returns true if the value is an object and *not* an array
function isObject(value) {
    return value instanceof Object && !(value instanceof Array)
}

var Part = function(field, operator, operand, parts, implicitField) {
    if(parts === undefined) parts = []

    this.field = field
    this.operator = operator
    this.operand = operand
    this.parts = parts
    this.implicitField = implicitField // only used for a certain type of $elemMatch part
}
<|endoftext|>