<commit_before>"use strict";
var
  extend = require("extend"),
  querystring = require("querystring"),
  objectPath = require("object-path"),
  util = require("util"),
  assert = require("chai").assert;

/**
 * Generates http client; wrapper of http/https library.
 *
 * `defaults` expect have two keys: params and headers
 *
 * @param {string} host
 * @param {boolean} ssl use to switch using `https` and `http`
 * @param {object} defaults will set unmodifiable default value
 * @return {object}
 */
function HttpClient(host, ssl, defaults) {
  function buildCookie(cookie) {
    var ret = cookie;
    if (typeof(cookie) === "object") {
      ret = cookie[0];
    }
    var idx = ret.indexOf(" expires=");
    if (idx != -1) {
      ret = ret.substring(0, idx);
    }
    return ret;
  }
  function urlencode(params) {
    function getValue(value) {
      if (typeof(value) === "function") {
        value = value(params);
      }
      return querystring.escape(value);
    }
    if (typeof(params) === "function") {
      params = params();
    }
    var ret = "";
    for (var key in params) {
      if (params.hasOwnProperty(key)) {
        var value = params[key];
        if (Array.isArray(value)) {
          for (var i=0; i<value.length; i++) {
            if (ret.length > 0) ret += "&";
            ret += querystring.escape(key) + "=" + getValue(value[i]);
          }
        } else {
          if (ret.length > 0) ret += "&";
          ret += querystring.escape(key) + "=" + getValue(value);
        }
      }
    }
    return ret;
  }
  function toJson(params) {
    function normalize(p) {
      var
        type = typeof(p),
        ret = p;
      if (type === "function") {
        p = p();
        type = typeof(p);
      }
      if (type === "object") {
        if (Array.isArray(p)) {
          ret = [];
          for (var i=0; i<p.length; i++) {
            ret.push(normalize(p[i]));
          }
        } else {
          ret = {};
          for (var key in p) {
            if (p.hasOwnProperty(key)) {
              ret[key] = normalize(p[key]);
            }
          }
        }
      }
      return ret;
    }
    if (typeof(params) === "function") {
      params = params();
    }
    params = normalize(params);
    return JSON.stringify(params);
  }
  function withAPI(v) {
    api = v;
    return this;
  }
  function withParams(v) {
    params = v;
    return this;
  }
  function withHeaders(v) {
    headers = v;
    return this;
  }
  function done(callback, data, res, req) {
    function isDone(func) {
      return callback.toString().indexOf("done() invoked with non-Error") !== -1;
    }
    if (!callback) {
      return;
    }
    if (isDone(callback)) {
      callback();
    } else {
      callback(data, res, req);
    }
  }
  function login(params) {
    return self.api(api.login).params(params);
  }
  function success(callback) {
    return doTest(function(data, res, req) {
      assert.ok(api.isSuccess(data, res), "Response isn't success.");
      api.validateResponse(data, res, req);
      done(callback, data, res, req);
    });
  }
  function badRequest(callback) {
    return doTest(function(data, res, req) {
      assert.ok(api.isBadRequest(data, res), "Response isn't badRequest.");
      done(callback, data, res, req);
    });
  }
  function notFound(callback) {
    return doTest(function(data, res, req) {
      assert.ok(api.isNotFound(data, res), "Response isn't notFound.");
      done(callback, data, res, req);
    });
  }
  function unauthorized(callback) {
    return doTest(function(data, res, req) {
      assert.ok(api.isUnauthorized(data, res), "Response isn't unauthorized.");
      done(callback, data, res, req);
    });
  }
  function forbidden(callback) {
    return doTest(function(data, res, req) {
      assert.ok(api.isForbidden(data, res), "Response isn't unauthorized.");
      done(callback, data, res, req);
    });
  }
  function clientError(callback) {
    return doTest(function(data, res, req) {
      assert.ok(api.isClientError(data, res), "Response isn't clientError.");
      done(callback, data, res, req);
    });
  }
  function doTest(callback) {
    function isJson(contentType) {
      if (!contentType) return false;
      var ct = contentType.toLowerCase();
      return ct.indexOf("/json") > 0 || ct.indexOf("+json") > 0;
    }
    function doCallback(data, res, req) {
      if (currentApi.verbose()) {
        console.log("**** Response Data ****");
        console.log("statusCode = " + res.statusCode);
        console.log("data = ");
        console.log(data);
      }
      if (!callback) {
        return;
      }
      var ct = res.headers["content-type"];
      if (isJson(ct)) {
        data = JSON.parse(data);
      }
      callback(data, res, req);
    }
    function buildEndpoint() {
      var ret = currentApi.endpoint;
      if (currentApi.urlParams) {
        currentApi.urlParams.forEach(function(key) {
          ret = ret.replace("[" + key + "]", currentParams[key]);
          delete currentParams[key];
        });
      }
      return ret;
    }
    var
      currentApi = api,
      currentParams = extend({}, defaults.params, params),
      currentHeaders = extend({}, defaults.headers, headers);
    params = null;
    headers = null;
    if (!currentApi) {
      throw "api isn't set.";
    }
    var requestParams = {
      "method": currentApi.method,
      "headers": currentHeaders,
      "path": buildEndpoint(),
      "data": Object.keys(currentParams).length > 0 ? currentParams : null,
      "log": currentApi.verbose()
    };
    if (currentApi.request && currentApi.request.headers) {
      extend(requestParams.headers, currentApi.request.headers);
    }
    if (currentApi.request && currentApi.request.contentType) {
      requestParams.headers["Content-Type"] = currentApi.request.contentType;
    }
    request(requestParams, doCallback);
    return self;
  }
  function request(config, callback, log) {
    var data = null;

    config.headers = config.headers || {};

    if (config.method == "GET" && config.data && !config.params) {
      config.params = config.data;
      delete config.data;
    }
    if (config.data) {
      var ct = config.headers["Content-Type"];
      if (!ct) {
        ct = "application/x-www-form-urlencoded";
        config.headers["Content-Type"] = ct;
      }
      if (ct === "application/x-www-form-urlencoded") {
        data = urlencode(config.data);
      } else if (ct === "application/json") {
        data = toJson(config.data);
      }
      config.headers["Content-Length"] = Buffer.byteLength(data, "utf8");
    }
    if (cookie) {
      config.headers.Cookie = buildCookie(cookie);
    }
    if (config.params) {
      config.path += "?" + urlencode(config.params);
    }
    if (config.log) {
      console.log("**** Request Data ****");
      console.log(config.path);
      if (data) {
        console.log(data);
      }
    }
    config.hostname = host;
    config.port = port;
    var req = con.request(config, function(res) {
      var body = "";
      res.setEncoding("utf-8");
      if (res.headers && res.headers["set-cookie"]) {
        cookie = res.headers["set-cookie"];
      }
      res.on("data", function(data) { body += data;});
      res.on("end", function() {
        if (callback) {
          callback(body, res, req);
        }
      });
    });
    req.on("error", function(e) {
      console.log("!!!!! error !!!!!", e);
    });
    if (data) {
      req.write(data);
    }
    req.end();
  }
  function copy() {
    var ret = new HttpClient(host + ":" + port, ssl, defaults);
    ret.assign(cookie, api, params, headers);
    return ret;
  }
  function assign(cookie_, api_, params_, headers_) {
    cookie = cookie_;
    api = api_;
    params = params_;
    headers = headers_;
  }
  var
    self = this,
    con = ssl ? require("https") : require("http"),
    port = ssl ? 443 : 80,
    cookie = null,
    api = null,
    params = null,
    headers = null;

  // Initialize default parameter object
  defaults = defaults || {};
  defaults.params = defaults.params || {};
  defaults.headers = defaults.headers || {};

  if (host.indexOf(":") != -1) {
    port = parseInt(host.split(":")[1], 10);
    host = host.split(":")[0];
  }
  function badRequestAll(runDefaults, optionParams) {
    function doIt(desc, data) {
      it(desc, function(done) {
        copy().params(data).badRequest(done);
      });
    }
    function processValue(desc, key, value) {
      var
        data = extend({}, params),
        orgValue = objectPath.get(data, key);
      if (util.isArray(orgValue)) {
        orgValue[0] = value;
      } else {
        objectPath.set(data, key, value);
      }
      doIt(desc, data);
    }
    function process(prefix, reqParams) {
      reqParams.forEach(function(p) {
        var
          fullName = prefix ? prefix + "." + p.name : p.name,
          options = {};
        if (p.hasRule("email")) {
          options.email = true;
        }
        if (p.hasRule("url")) {
          options.url = true;
        }
        if (runDefaults) {
          var invalidTypeValue = p.generateInvalidTypeValue();
          if (invalidTypeValue) {
            processValue(fullName + " - invalid datatype(" + invalidTypeValue + ")",
              fullName, invalidTypeValue);
          }
          p.rules.forEach(function(r) {
            var data = r.generateBadRequest(fullName, params, options);
            if (data) {
              doIt(fullName + " - " + r.name　+ "(" + objectPath.get(data, fullName) + ")", data);
            }
          });
        }
        var optionValue = objectPath.get(optionParams, fullName);
        if (optionValue) {
          if (util.isArray(optionValue)) {
            optionValue.forEach(function(v) {
              processValue(fullName + " - " + v, fullName, v);
            });
          } else {
            processValue(fullName + " - " + optionValue, fullName, optionValue);
          }
        }
        process(fullName, p.childParams());

      });
    }
    if (!api || !params) {
      throw "Must specify api and params";
    }
    switch (arguments.length) {
      case 0:
        runDefaults = true;
        optionParams = {};
        break;
      case 1:
        if (typeof(runDefaults) === "object") {
          optionParams = runDefaults;
          runDefaults = true;
        }
        break;
    }
    optionParams = optionParams || {};
    describe("BadRequest test for", function() {
      process("", api.request.params.childParams());
    });
  }
  extend(this, {
    "api": withAPI,
    "params": withParams,
    "headers": withHeaders,
    "login": login,
    "success": success,
    "badRequest": badRequest,
    "notFound": notFound,
    "unauthorized": unauthorized,
    "forbidden": forbidden,
    "clientError": clientError,
    "request": request,
    "assign": assign,
    "badRequestAll": badRequestAll
  });
}

module.exports = HttpClient;
<commit_msg>Mod error message<commit_after>"use strict";
var
  extend = require("extend"),
  querystring = require("querystring"),
  objectPath = require("object-path"),
  util = require("util"),
  assert = require("chai").assert;

/**
 * Generates http client; wrapper of http/https library.
 *
 * `defaults` expect have two keys: params and headers
 *
 * @param {string} host
 * @param {boolean} ssl use to switch using `https` and `http`
 * @param {object} defaults will set unmodifiable default value
 * @return {object}
 */
function HttpClient(host, ssl, defaults) {
  function buildCookie(cookie) {
    var ret = cookie;
    if (typeof(cookie) === "object") {
      ret = cookie[0];
    }
    var idx = ret.indexOf(" expires=");
    if (idx != -1) {
      ret = ret.substring(0, idx);
    }
    return ret;
  }
  function urlencode(params) {
    function getValue(value) {
      if (typeof(value) === "function") {
        value = value(params);
      }
      return querystring.escape(value);
    }
    if (typeof(params) === "function") {
      params = params();
    }
    var ret = "";
    for (var key in params) {
      if (params.hasOwnProperty(key)) {
        var value = params[key];
        if (Array.isArray(value)) {
          for (var i=0; i<value.length; i++) {
            if (ret.length > 0) ret += "&";
            ret += querystring.escape(key) + "=" + getValue(value[i]);
          }
        } else {
          if (ret.length > 0) ret += "&";
          ret += querystring.escape(key) + "=" + getValue(value);
        }
      }
    }
    return ret;
  }
  function toJson(params) {
    function normalize(p) {
      var
        type = typeof(p),
        ret = p;
      if (type === "function") {
        p = p();
        type = typeof(p);
      }
      if (type === "object") {
        if (Array.isArray(p)) {
          ret = [];
          for (var i=0; i<p.length; i++) {
            ret.push(normalize(p[i]));
          }
        } else {
          ret = {};
          for (var key in p) {
            if (p.hasOwnProperty(key)) {
              ret[key] = normalize(p[key]);
            }
          }
        }
      }
      return ret;
    }
    if (typeof(params) === "function") {
      params = params();
    }
    params = normalize(params);
    return JSON.stringify(params);
  }
  function withAPI(v) {
    api = v;
    return this;
  }
  function withParams(v) {
    params = v;
    return this;
  }
  function withHeaders(v) {
    headers = v;
    return this;
  }
  function done(callback, data, res, req) {
    function isDone(func) {
      return callback.toString().indexOf("done() invoked with non-Error") !== -1;
    }
    if (!callback) {
      return;
    }
    if (isDone(callback)) {
      callback();
    } else {
      callback(data, res, req);
    }
  }
  function login(params) {
    return self.api(api.login).params(params);
  }
  function stringify(data) {
    if (typeof(data) === "object") {
      return JSON.stringify(data);
    }
    return "" + data;
  }
  function success(callback) {
    return doTest(function(data, res, req) {
      assert(api.isSuccess(data, res), `StatusCode=${res.statusCode}. Response isn't Success.\n${stringify(data)}`);
      api.validateResponse(data, res, req);
      done(callback, data, res, req);
    });
  }
  function badRequest(callback) {
    return doTest(function(data, res, req) {
      assert(api.isBadRequest(data, res), `StatusCode=${res.statusCode}. Response isn't BadRequest.\n${stringify(data)}`);
      done(callback, data, res, req);
    });
  }
  function notFound(callback) {
    return doTest(function(data, res, req) {
      assert(api.isNotFound(data, res), `StatusCode=${res.statusCode}. Response isn't NotFound.\n${stringify(data)}`);
      done(callback, data, res, req);
    });
  }
  function unauthorized(callback) {
    return doTest(function(data, res, req) {
      assert(api.isUnauthorized(data, res), `StatusCode=${res.statusCode}. Response isn't Unauthorized.\n${stringify(data)}`);
      done(callback, data, res, req);
    });
  }
  function forbidden(callback) {
    return doTest(function(data, res, req) {
      assert(api.isForbidden(data, res), `StatusCode=${res.statusCode}. Response isn't Forbidden.\n${stringify(data)}`);
      done(callback, data, res, req);
    });
  }
  function clientError(callback) {
    return doTest(function(data, res, req) {
      assert(api.isClientError(data, res), `StatusCode=${res.statusCode}. Response isn't ClientError.\n${stringify(data)}`);
      done(callback, data, res, req);
    });
  }
  function doTest(callback) {
    function isJson(contentType) {
      if (!contentType) return false;
      var ct = contentType.toLowerCase();
      return ct.indexOf("/json") > 0 || ct.indexOf("+json") > 0;
    }
    function doCallback(data, res, req) {
      if (currentApi.verbose()) {
        console.log("**** Response Data ****");
        console.log("statusCode = " + res.statusCode);
        console.log("data = ");
        console.log(data);
      }
      if (!callback) {
        return;
      }
      var ct = res.headers["content-type"];
      if (isJson(ct)) {
        data = JSON.parse(data);
      }
      callback(data, res, req);
    }
    function buildEndpoint() {
      var ret = currentApi.endpoint;
      if (currentApi.urlParams) {
        currentApi.urlParams.forEach(function(key) {
          ret = ret.replace("[" + key + "]", currentParams[key]);
          delete currentParams[key];
        });
      }
      return ret;
    }
    var
      currentApi = api,
      currentParams = extend({}, defaults.params, params),
      currentHeaders = extend({}, defaults.headers, headers);
    params = null;
    headers = null;
    if (!currentApi) {
      throw "api isn't set.";
    }
    var requestParams = {
      "method": currentApi.method,
      "headers": currentHeaders,
      "path": buildEndpoint(),
      "data": Object.keys(currentParams).length > 0 ? currentParams : null,
      "log": currentApi.verbose()
    };
    if (currentApi.request && currentApi.request.headers) {
      extend(requestParams.headers, currentApi.request.headers);
    }
    if (currentApi.request && currentApi.request.contentType) {
      requestParams.headers["Content-Type"] = currentApi.request.contentType;
    }
    request(requestParams, doCallback);
    return self;
  }
  function request(config, callback, log) {
    var data = null;

    config.headers = config.headers || {};

    if (config.method == "GET" && config.data && !config.params) {
      config.params = config.data;
      delete config.data;
    }
    if (config.data) {
      var ct = config.headers["Content-Type"];
      if (!ct) {
        ct = "application/x-www-form-urlencoded";
        config.headers["Content-Type"] = ct;
      }
      if (ct === "application/x-www-form-urlencoded") {
        data = urlencode(config.data);
      } else if (ct === "application/json") {
        data = toJson(config.data);
      }
      config.headers["Content-Length"] = Buffer.byteLength(data, "utf8");
    }
    if (cookie) {
      config.headers.Cookie = buildCookie(cookie);
    }
    if (config.params) {
      config.path += "?" + urlencode(config.params);
    }
    if (config.log) {
      console.log("**** Request Data ****");
      console.log(config.path);
      if (data) {
        console.log(data);
      }
    }
    config.hostname = host;
    config.port = port;
    var req = con.request(config, function(res) {
      var body = "";
      res.setEncoding("utf-8");
      if (res.headers && res.headers["set-cookie"]) {
        cookie = res.headers["set-cookie"];
      }
      res.on("data", function(data) { body += data;});
      res.on("end", function() {
        if (callback) {
          callback(body, res, req);
        }
      });
    });
    req.on("error", function(e) {
      console.log("!!!!! error !!!!!", e);
    });
    if (data) {
      req.write(data);
    }
    req.end();
  }
  function copy() {
    var ret = new HttpClient(host + ":" + port, ssl, defaults);
    ret.assign(cookie, api, params, headers);
    return ret;
  }
  function assign(cookie_, api_, params_, headers_) {
    cookie = cookie_;
    api = api_;
    params = params_;
    headers = headers_;
  }
  var
    self = this,
    con = ssl ? require("https") : require("http"),
    port = ssl ? 443 : 80,
    cookie = null,
    api = null,
    params = null,
    headers = null;

  // Initialize default parameter object
  defaults = defaults || {};
  defaults.params = defaults.params || {};
  defaults.headers = defaults.headers || {};

  if (host.indexOf(":") != -1) {
    port = parseInt(host.split(":")[1], 10);
    host = host.split(":")[0];
  }
  function badRequestAll(runDefaults, optionParams) {
    function doIt(desc, data) {
      it(desc, function(done) {
        copy().params(data).badRequest(done);
      });
    }
    function processValue(desc, key, value) {
      var
        data = extend({}, params),
        orgValue = objectPath.get(data, key);
      if (util.isArray(orgValue)) {
        orgValue[0] = value;
      } else {
        objectPath.set(data, key, value);
      }
      doIt(desc, data);
    }
    function process(prefix, reqParams) {
      reqParams.forEach(function(p) {
        var
          fullName = prefix ? prefix + "." + p.name : p.name,
          options = {};
        if (p.hasRule("email")) {
          options.email = true;
        }
        if (p.hasRule("url")) {
          options.url = true;
        }
        if (runDefaults) {
          var invalidTypeValue = p.generateInvalidTypeValue();
          if (invalidTypeValue) {
            processValue(fullName + " - invalid datatype(" + invalidTypeValue + ")",
              fullName, invalidTypeValue);
          }
          p.rules.forEach(function(r) {
            var data = r.generateBadRequest(fullName, params, options);
            if (data) {
              doIt(fullName + " - " + r.name　+ "(" + objectPath.get(data, fullName) + ")", data);
            }
          });
        }
        var optionValue = objectPath.get(optionParams, fullName);
        if (optionValue) {
          if (util.isArray(optionValue)) {
            optionValue.forEach(function(v) {
              processValue(fullName + " - " + v, fullName, v);
            });
          } else {
            processValue(fullName + " - " + optionValue, fullName, optionValue);
          }
        }
        process(fullName, p.childParams());

      });
    }
    if (!api || !params) {
      throw "Must specify api and params";
    }
    switch (arguments.length) {
      case 0:
        runDefaults = true;
        optionParams = {};
        break;
      case 1:
        if (typeof(runDefaults) === "object") {
          optionParams = runDefaults;
          runDefaults = true;
        }
        break;
    }
    optionParams = optionParams || {};
    describe("BadRequest test for", function() {
      process("", api.request.params.childParams());
    });
  }
  extend(this, {
    "api": withAPI,
    "params": withParams,
    "headers": withHeaders,
    "login": login,
    "success": success,
    "badRequest": badRequest,
    "notFound": notFound,
    "unauthorized": unauthorized,
    "forbidden": forbidden,
    "clientError": clientError,
    "request": request,
    "assign": assign,
    "badRequestAll": badRequestAll
  });
}

module.exports = HttpClient;
<|endoftext|>


<commit_before>var hagent = require('hagent');
var tls = require('tls');
var ca = require('./ca');

var serverAgent = hagent.serverAgent;
serverAgent.createCertificate = ca.createCertificate;
exports.serverAgent = serverAgent;

var createTLSServer = function(options, listener) {
  return tls.createServer(options, listener);
};
var getTLSServer = hagent.create(createTLSServer, 43900);
var callbacks = [];
var TIMEOUT = 6000;
var sniPort;

var SNICallback = function(serverName, cb) {
  serverName = ca.getDomain(serverName);
  var options = ca.createCertificate(serverName);
  if (!options.ctx) {
    options._ctx = ca.createSecureContext(options);
  }
  cb(null, options._ctx);
};

var removeServer = function() {
  sniPort = null;
  try {
    this.close();
  } catch(e) {} //重复关闭会导致异常
};

exports.getSNIServer = function(listener, callback) {
  if (sniPort) {
    return callback(sniPort);
  }
  getTLSServer({ SNICallback: SNICallback }, listener, function(server, port) {
    server.on('error', removeServer);
    var timeout = setTimeout(removeServer, TIMEOUT);
    var clearup = function() {
      clearTimeout(timeout);
    };
    server.once('tlsClientError', clearup);
    server.once('secureConnection', clearup);
    sniPort = port;
    callbacks.forEach(function(cb) {
      cb(sniPort);
    });
  });
};

exports.getDomain = ca.getDomain;
exports.existsCustomCert = ca.existsCustomCert;
exports.getRootCAFile = ca.getRootCAFile;
<commit_msg>refactor: code<commit_after>var hagent = require('hagent');
var tls = require('tls');
var ca = require('./ca');

var serverAgent = hagent.serverAgent;
serverAgent.createCertificate = ca.createCertificate;
exports.serverAgent = serverAgent;

var createTLSServer = function(options, listener) {
  return tls.createServer(options, listener);
};
var getTLSServer = hagent.create(createTLSServer, 43900);
var callbacks = [];
var TIMEOUT = 6000;
var sniPort;

var SNICallback = function(serverName, cb) {
  serverName = ca.getDomain(serverName);
  var options = ca.createCertificate(serverName);
  if (!options._ctx) {
    options._ctx = ca.createSecureContext(options);
  }
  cb(null, options._ctx);
};

var removeServer = function() {
  sniPort = null;
  try {
    this.close();
  } catch(e) {} //重复关闭会导致异常
};

exports.getSNIServer = function(listener, callback) {
  if (sniPort) {
    return callback(sniPort);
  }
  getTLSServer({ SNICallback: SNICallback }, listener, function(server, port) {
    server.on('error', removeServer);
    var timeout = setTimeout(removeServer, TIMEOUT);
    var clearup = function() {
      clearTimeout(timeout);
    };
    server.once('tlsClientError', clearup);
    server.once('secureConnection', clearup);
    sniPort = port;
    callbacks.forEach(function(cb) {
      cb(sniPort);
    });
  });
};

exports.getDomain = ca.getDomain;
exports.existsCustomCert = ca.existsCustomCert;
exports.getRootCAFile = ca.getRootCAFile;
<|endoftext|>


<commit_before>var log = require('ringo/logging').getLogger(module.id);
var {XmlConfiguration} = org.eclipse.jetty.xml;
var {Server, HttpConfiguration, HttpConnectionFactory,
        ServerConnector, SslConnectionFactory,
        SecureRequestCustomizer} = org.eclipse.jetty.server;
var {HandlerCollection, ContextHandlerCollection} = org.eclipse.jetty.server.handler;
var {HTTP_1_1, HTTP_2} = org.eclipse.jetty.http.HttpVersion;
var {HashSessionIdManager} = org.eclipse.jetty.server.session;
var {SslContextFactory} = org.eclipse.jetty.util.ssl;
var objects = require("ringo/utils/objects");
var ApplicationContext = require("./context/application");
var StaticContext = require("./context/static");
var fs = require("fs");

var HttpServer = module.exports = function HttpServer(xmlPath) {
    if (!(this instanceof HttpServer)) {
        return new HttpServer(xmlPath);
    }

    var jetty = new Server();
    jetty.setStopAtShutdown(true);
    jetty.setStopTimeout(1000);
    jetty.setDumpAfterStart(false);
    jetty.setDumpBeforeStop(false);

    var xmlConfig = null;

    Object.defineProperties(this, {
        "jetty": {
            "value": jetty,
            "enumerable": true
        },
        "xmlConfig": {
            "get": function() {
                return xmlConfig;
            },
            "set": function(config) {
                if (!(config instanceof XmlConfiguration)) {
                    throw new Error("Invalid jetty xml configuration");
                }
                xmlConfig = config;
                xmlConfig.configure(jetty);
            },
            "enumerable": true
        },
        "contexts": {
            "value": {},
            "enumerable": true
        }
    });

    if (xmlPath != null) {
        this.configure(xmlPath);
    }
    return this;
};

HttpServer.prototype.toString = function() {
    return "[HttpServer]";
};

HttpServer.prototype.configure = function(xmlPath) {
    var xmlResource = getResource(xmlPath);
    if (!xmlResource.exists()) {
        throw Error('Jetty XML configuration "' + xmlResource + '" not found');
    }
    return this.xmlConfig = new XmlConfiguration(xmlResource.inputStream);
};

HttpServer.prototype.createHttpConfig = function(options) {
    options = objects.merge(options || {}, {
        "requestHeaderSize": 8129,
        "outputBufferSize": 32768,
        "responseHeaderSize": 8129
    });
    var httpConfig = new HttpConfiguration();
    httpConfig.setRequestHeaderSize(options.requestHeaderSize);
    httpConfig.setOutputBufferSize(options.outputBufferSize);
    httpConfig.setResponseHeaderSize(options.responseHeaderSize);
    httpConfig.setSecureScheme("https");
    return httpConfig;
};

HttpServer.prototype.createConnector = function(connectionFactory, options) {
    var connector = new ServerConnector(this.jetty, options.acceptors || -1,
            options.selectors || -1, connectionFactory);
    connector.setHost(options.host);
    connector.setPort(options.port);
    connector.setIdleTimeout(options.idleTimeout || 30000);
    connector.setSoLingerTime(options.soLingerTime || -1);
    connector.setAcceptorPriorityDelta(options.acceptorPriorityDelta || 0);
    connector.setSelectorPriorityDelta(options.selectorPriorityDelta || 0);
    connector.setAcceptQueueSize(options.acceptQueueSize || 0);
    if (typeof(options.name) === "string") {
        connector.setName(options.name);
    }
    return connector;
};

HttpServer.prototype.createHttpConnector = function(options) {
    options = objects.merge(options || {}, {
        "host": "0.0.0.0",
        "port": 8080
    });
    var httpConfig = this.createHttpConfig(options);
    var connectionFactory = new HttpConnectionFactory(httpConfig);
    return this.createConnector(connectionFactory, options);
};

HttpServer.prototype.createHttpsConnector = function(options) {
    options = objects.merge(options || {}, {
        "host": "0.0.0.0",
        "port": 8080,
        "excludeSipherSuites": [
            "SSL_RSA_WITH_DES_CBC_SHA",
            "SSL_DHE_RSA_WITH_DES_CBC_SHA", "SSL_DHE_DSS_WITH_DES_CBC_SHA",
            "SSL_RSA_EXPORT_WITH_RC4_40_MD5",
            "SSL_RSA_EXPORT_WITH_DES40_CBC_SHA",
            "SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA",
            "SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA"
        ]
    });
    var sslContextFactory = new SslContextFactory();
    sslContextFactory.setKeyStorePath(options.keyStore);
    sslContextFactory.setKeyStoreType(options.keyStoreType || "JKS");
    sslContextFactory.setKeyStorePassword(options.keyStorePassword);
    sslContextFactory.setKeyManagerPassword(options.keyManagerPassword);
    sslContextFactory.setTrustStorePath(options.trustStore || options.keyStore);
    sslContextFactory.setTrustStorePassword(options.trustStorePassword ||
            options.keyStorePassword);
    sslContextFactory.setExcludeCipherSuites(options.excludeCipherSuites);
    var sslConnectionFactory = new SslConnectionFactory(sslContextFactory,
            HTTP_1_1.toString());
    var httpsConfig = this.createHttpConfig(options);
    httpsConfig.addCustomizer(new SecureRequestCustomizer());
    var httpConnectionFactory = new HttpConnectionFactory(httpsConfig);
    return this.createConnector([sslConnectionFactory, httpConnectionFactory], options);
};

HttpServer.prototype.createHttpListener = function(options) {
    var connector = this.createHttpConnector(options);
    this.jetty.addConnector(connector);
    return connector;
};

HttpServer.prototype.createHttpsListener = function(options) {
    var connector = this.createHttpsConnector(options);
    this.jetty.addConnector(connector);
    return connector;
};

HttpServer.prototype.getHandlerCollection = function() {
    var handlerCollection = this.jetty.getHandler();
    if (handlerCollection === null) {
        handlerCollection = new HandlerCollection();
        this.jetty.setHandler(handlerCollection);
    }
    return handlerCollection;
};

HttpServer.prototype.getContextHandlerCollection = function() {
    var handlerCollection = this.getHandlerCollection();
    var contextHandlerCollection =
            handlerCollection.getChildHandlerByClass(ContextHandlerCollection);
    if (contextHandlerCollection === null) {
        contextHandlerCollection = new ContextHandlerCollection();
        handlerCollection.addHandler(contextHandlerCollection);
    }
    return contextHandlerCollection;
};

HttpServer.prototype.addContext = function(context) {
    this.contexts[context.getKey()] = context;
    if (this.jetty.isRunning()) {
        context.contextHandler.start();
    }
    return context;
};

HttpServer.prototype.enableSessions = function(options) {
    options || (options = {});
    var sessionIdManager = new HashSessionIdManager(options.random || null);
    sessionIdManager.setWorkerName(options.name || "node1");
    this.jetty.setSessionIdManager(sessionIdManager);
    return sessionIdManager;
};

HttpServer.prototype.serveApplication = function(mountpoint, app, options) {
    if (typeof(mountpoint) !== "string") {
        throw new Error("Missing mountpoint argument");
    }
    if (app == null) {
        throw new Error("Missing application to serve");
    } else if (typeof(app) !== "function" && typeof(app) !== "string") {
        throw new Error("Application must be either a function or the path " +
                "to a module exporting an 'app' function");
    }
    options || (options = {});
    options = {
        "security": options.security !== false,
        "sessions": options.sessions !== false,
        "cookieName": options.cookieName || null,
        "cookieDomain": options.cookieDomain || null,
        "cookiePath": options.cookiePath || null,
        "httpOnlyCookies": options.httpOnlyCookies !== false,
        "secureCookies": options.secureCookies === true,
        "statistics": options.statistics === true,
        "virtualHosts": options.virtualHosts
    };
    var parentContainer = this.getContextHandlerCollection();
    var context = new ApplicationContext(parentContainer, mountpoint, options);
    if (typeof app === "function") {
        context.serve(app);
    } else {
        // FIXME: appName "app" is determined by JsgiServlet/jsgiConnector
        context.serve({
            "appModule": app,
            "appName": "app"
        });
    }
    return this.addContext(context);
};

HttpServer.prototype.serveStatic = function(mountpoint, directory, options) {
    if (typeof(mountpoint) !== "string") {
        throw new Error("Missing mountpoint argument");
    }
    if (typeof(directory) !== "string") {
        throw new Error("Missing directory argument");
    } else if (!fs.exists(directory) || !fs.isDirectory(directory)) {
        throw new Error("Directory '" + directory + "' doesn't exist or is not a directory");
    }
    options || (options = {});
    var initParameters = {
        "acceptRanges": options.acceptRanges === true,
        "dirAllowed": options.allowDirectoryListing === true,
        "gzip": options.gzip === true,
        "stylesheet": options.stylesheet || null,
        "etags": options.etags !== false,
        "maxCacheSize": options.maxCacheSize || 0,
        "maxCachedFileSize": options.maxCachedFileSize || 0,
        "maxCachedFiles": options.maxCachedFiles || 0,
        "cacheControl": options.cacheControl || null,
        "otherGzipFileExtensions": options.gzipExtensions || null
    };
    var parentContainer = this.getContextHandlerCollection();
    var context = new StaticContext(parentContainer, mountpoint, {
            "security": options.security === true,
            "sessions": options.sessions === true,
            "virtualHosts": options.virtualHosts
        });
    context.serve(directory, initParameters);
    return this.addContext(context);
};

HttpServer.prototype.start = function() {
    this.jetty.start();
    for each (let connector in this.jetty.getConnectors()) {
        log.info("Server on {}:{} started", connector.getHost(), connector.getPort());
    }
};

HttpServer.prototype.stop = function() {
    return this.jetty.stop();
};

HttpServer.prototype.destroy = function() {
    return this.jetty.destroy();
};

HttpServer.prototype.isRunning = function() {
    return this.jetty.isRunning();
};<commit_msg>- disable server version response header by default - enable date response header by default - added experimental http2 connector configuration methods<commit_after>var log = require('ringo/logging').getLogger(module.id);
var {XmlConfiguration} = org.eclipse.jetty.xml;
var {Server, HttpConfiguration, HttpConnectionFactory,
        ServerConnector, SslConnectionFactory,
        SecureRequestCustomizer} = org.eclipse.jetty.server;
var {HandlerCollection, ContextHandlerCollection} = org.eclipse.jetty.server.handler;
var {HTTP_1_1} = org.eclipse.jetty.http.HttpVersion;
var {HashSessionIdManager} = org.eclipse.jetty.server.session;
var {SslContextFactory} = org.eclipse.jetty.util.ssl;
var {HTTP2ServerConnectionFactory} = org.eclipse.jetty.http2.server;
var {ALPNServerConnectionFactory} = org.eclipse.jetty.alpn.server;
var {HTTP2Cipher} = org.eclipse.jetty.http2;

var objects = require("ringo/utils/objects");
var ApplicationContext = require("./context/application");
var StaticContext = require("./context/static");
var fs = require("fs");

var HttpServer = module.exports = function HttpServer(xmlPath) {
    if (!(this instanceof HttpServer)) {
        return new HttpServer(xmlPath);
    }

    var jetty = new Server();
    jetty.setStopAtShutdown(true);
    jetty.setStopTimeout(1000);
    jetty.setDumpAfterStart(false);
    jetty.setDumpBeforeStop(false);

    var xmlConfig = null;

    Object.defineProperties(this, {
        "jetty": {
            "value": jetty,
            "enumerable": true
        },
        "xmlConfig": {
            "get": function() {
                return xmlConfig;
            },
            "set": function(config) {
                if (!(config instanceof XmlConfiguration)) {
                    throw new Error("Invalid jetty xml configuration");
                }
                xmlConfig = config;
                xmlConfig.configure(jetty);
            },
            "enumerable": true
        },
        "contexts": {
            "value": {},
            "enumerable": true
        }
    });

    if (xmlPath != null) {
        this.configure(xmlPath);
    }
    return this;
};

HttpServer.prototype.toString = function() {
    return "[HttpServer]";
};

HttpServer.prototype.configure = function(xmlPath) {
    var xmlResource = getResource(xmlPath);
    if (!xmlResource.exists()) {
        throw Error('Jetty XML configuration "' + xmlResource + '" not found');
    }
    return this.xmlConfig = new XmlConfiguration(xmlResource.inputStream);
};

HttpServer.prototype.createHttpConfig = function(options) {
    options = objects.merge(options || {}, {
        "requestHeaderSize": 8129,
        "outputBufferSize": 32768,
        "responseHeaderSize": 8129
    });
    var httpConfig = new HttpConfiguration();
    httpConfig.setRequestHeaderSize(options.requestHeaderSize);
    httpConfig.setOutputBufferSize(options.outputBufferSize);
    httpConfig.setResponseHeaderSize(options.responseHeaderSize);
    httpConfig.setSecureScheme("https");
    httpConfig.setSendServerVersion(options.sendServerVersion === true);
    httpConfig.setSendDateHeader(options.sendDateHeader !== false);
    return httpConfig;
};

HttpServer.prototype.createConnector = function(connectionFactory, options) {
    var connector = new ServerConnector(this.jetty, options.acceptors || -1,
            options.selectors || -1, connectionFactory);
    connector.setHost(options.host);
    connector.setPort(options.port);
    connector.setIdleTimeout(options.idleTimeout || 30000);
    connector.setSoLingerTime(options.soLingerTime || -1);
    connector.setAcceptorPriorityDelta(options.acceptorPriorityDelta || 0);
    connector.setSelectorPriorityDelta(options.selectorPriorityDelta || 0);
    connector.setAcceptQueueSize(options.acceptQueueSize || 0);
    if (typeof(options.name) === "string") {
        connector.setName(options.name);
    }
    return connector;
};

HttpServer.prototype.createHttpConnector = function(options) {
    options = objects.merge(options || {}, {
        "host": "0.0.0.0",
        "port": 8080
    });
    var httpConfig = this.createHttpConfig(options);
    var connectionFactory = new HttpConnectionFactory(httpConfig);
    return this.createConnector(connectionFactory, options);
};

HttpServer.prototype.createSslContextFactory = function(options) {
    options = objects.merge(options || {}, {
        "includeCipherSuites": [],
        "excludeCipherSuites": [
            "SSL_RSA_WITH_DES_CBC_SHA",
            "SSL_DHE_RSA_WITH_DES_CBC_SHA", "SSL_DHE_DSS_WITH_DES_CBC_SHA",
            "SSL_RSA_EXPORT_WITH_RC4_40_MD5",
            "SSL_RSA_EXPORT_WITH_DES40_CBC_SHA",
            "SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA",
            "SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA"
        ],
        "includeProtocols": ["TLSv1", "TLSv1.1", "TLSv1.2"],
        "excludeProtocols": ["SSLv3", "SSLv2Hello"]
    });
    var sslContextFactory = new SslContextFactory();
    sslContextFactory.setKeyStorePath(options.keyStore);
    sslContextFactory.setKeyStoreType(options.keyStoreType || "JKS");
    sslContextFactory.setKeyStorePassword(options.keyStorePassword);
    sslContextFactory.setKeyManagerPassword(options.keyManagerPassword);
    sslContextFactory.setTrustStorePath(options.trustStore || options.keyStore);
    sslContextFactory.setTrustStorePassword(options.trustStorePassword ||
            options.keyStorePassword);
    sslContextFactory.setIncludeCipherSuites(options.includeCipherSuites);
    sslContextFactory.setExcludeCipherSuites(options.excludeCipherSuites);
    sslContextFactory.setIncludeProtocols(options.includeProtocols);
    sslContextFactory.setExcludeProtocols(options.excludeProtocols);
    return sslContextFactory;
};

HttpServer.prototype.createHttpsConnector = function(options) {
    options = objects.merge(options || {}, {
        "host": "0.0.0.0",
        "port": 8443
    });
    var sslContextFactory = this.createSslContextFactory(options);
    var sslConnectionFactory = new SslConnectionFactory(sslContextFactory,
            HTTP_1_1.toString());
    var httpsConfig = this.createHttpConfig(options);
    httpsConfig.addCustomizer(new SecureRequestCustomizer());
    var httpConnectionFactory = new HttpConnectionFactory(httpsConfig);
    return this.createConnector([sslConnectionFactory, httpConnectionFactory], options);
};

HttpServer.prototype.createHttp2Connector = function(options) {
    options = objects.merge(options || {}, {
        "host": "0.0.0.0",
        "port": 8443
    });
    var sslContextFactory = this.createSslContextFactory(options);
    sslContextFactory.setCipherComparator(new HTTP2Cipher.CipherComparator());
    sslContextFactory.setUseCipherSuitesOrder(true);
    var sslConnectionFactory = new SslConnectionFactory(sslContextFactory, "alpn");
    var httpsConfig = this.createHttpConfig(options);
    httpsConfig.addCustomizer(new SecureRequestCustomizer());
    var alpnConnectionFactory = new ALPNServerConnectionFactory("");
    //alpnConnectionFactory.setDefaultProtocol(HTTP_1_1.toString());
    var http2ConnectionFactory = new HTTP2ServerConnectionFactory(httpsConfig);
    http2ConnectionFactory.setMaxConcurrentStreams(1024);
    http2ConnectionFactory.setInitialStreamSendWindow(65535);
    //org.eclipse.jetty.alpn.ALPN.debug = true;
    return this.createConnector([sslConnectionFactory, alpnConnectionFactory,
            http2ConnectionFactory], options);
};

HttpServer.prototype.createHttpListener = function(options) {
    var connector = this.createHttpConnector(options);
    this.jetty.addConnector(connector);
    return connector;
};

HttpServer.prototype.createHttpsListener = function(options) {
    var connector = this.createHttpsConnector(options);
    this.jetty.addConnector(connector);
    return connector;
};

HttpServer.prototype.createHttp2Listener = function(options) {
    var connector = this.createHttp2Connector(options);
    this.jetty.addConnector(connector);
    return connector;
};

HttpServer.prototype.getHandlerCollection = function() {
    var handlerCollection = this.jetty.getHandler();
    if (handlerCollection === null) {
        handlerCollection = new HandlerCollection();
        this.jetty.setHandler(handlerCollection);
    }
    return handlerCollection;
};

HttpServer.prototype.getContextHandlerCollection = function() {
    var handlerCollection = this.getHandlerCollection();
    var contextHandlerCollection =
            handlerCollection.getChildHandlerByClass(ContextHandlerCollection);
    if (contextHandlerCollection === null) {
        contextHandlerCollection = new ContextHandlerCollection();
        handlerCollection.addHandler(contextHandlerCollection);
    }
    return contextHandlerCollection;
};

HttpServer.prototype.addContext = function(context) {
    this.contexts[context.getKey()] = context;
    if (this.jetty.isRunning()) {
        context.contextHandler.start();
    }
    return context;
};

HttpServer.prototype.enableSessions = function(options) {
    options || (options = {});
    var sessionIdManager = new HashSessionIdManager(options.random || null);
    sessionIdManager.setWorkerName(options.name || "node1");
    this.jetty.setSessionIdManager(sessionIdManager);
    return sessionIdManager;
};

HttpServer.prototype.serveApplication = function(mountpoint, app, options) {
    if (typeof(mountpoint) !== "string") {
        throw new Error("Missing mountpoint argument");
    }
    if (app == null) {
        throw new Error("Missing application to serve");
    } else if (typeof(app) !== "function" && typeof(app) !== "string") {
        throw new Error("Application must be either a function or the path " +
                "to a module exporting an 'app' function");
    }
    options || (options = {});
    options = {
        "security": options.security !== false,
        "sessions": options.sessions !== false,
        "cookieName": options.cookieName || null,
        "cookieDomain": options.cookieDomain || null,
        "cookiePath": options.cookiePath || null,
        "httpOnlyCookies": options.httpOnlyCookies !== false,
        "secureCookies": options.secureCookies === true,
        "statistics": options.statistics === true,
        "virtualHosts": options.virtualHosts
    };
    var parentContainer = this.getContextHandlerCollection();
    var context = new ApplicationContext(parentContainer, mountpoint, options);
    if (typeof app === "function") {
        context.serve(app);
    } else {
        // FIXME: appName "app" is determined by JsgiServlet/jsgiConnector
        context.serve({
            "appModule": app,
            "appName": "app"
        });
    }
    return this.addContext(context);
};

HttpServer.prototype.serveStatic = function(mountpoint, directory, options) {
    if (typeof(mountpoint) !== "string") {
        throw new Error("Missing mountpoint argument");
    }
    if (typeof(directory) !== "string") {
        throw new Error("Missing directory argument");
    } else if (!fs.exists(directory) || !fs.isDirectory(directory)) {
        throw new Error("Directory '" + directory + "' doesn't exist or is not a directory");
    }
    options || (options = {});
    var initParameters = {
        "acceptRanges": options.acceptRanges === true,
        "dirAllowed": options.allowDirectoryListing === true,
        "gzip": options.gzip === true,
        "stylesheet": options.stylesheet || null,
        "etags": options.etags !== false,
        "maxCacheSize": options.maxCacheSize || 0,
        "maxCachedFileSize": options.maxCachedFileSize || 0,
        "maxCachedFiles": options.maxCachedFiles || 0,
        "cacheControl": options.cacheControl || null,
        "otherGzipFileExtensions": options.gzipExtensions || null
    };
    var parentContainer = this.getContextHandlerCollection();
    var context = new StaticContext(parentContainer, mountpoint, {
            "security": options.security === true,
            "sessions": options.sessions === true,
            "virtualHosts": options.virtualHosts
        });
    context.serve(directory, initParameters);
    return this.addContext(context);
};

HttpServer.prototype.start = function() {
    this.jetty.start();
    for each (let connector in this.jetty.getConnectors()) {
        log.info("Server on {}:{} started", connector.getHost(), connector.getPort());
    }
};

HttpServer.prototype.stop = function() {
    return this.jetty.stop();
};

HttpServer.prototype.destroy = function() {
    return this.jetty.destroy();
};

HttpServer.prototype.isRunning = function() {
    return this.jetty.isRunning();
};<|endoftext|>


<commit_before>'use strict';

/**
 * Init system for Node.js, main file.
 * (C) 2015 Alex Fernández.
 */

// requires
require('prototypes');
var Log = require('log');
var util = require('util');
var events = require('events');
var domain = require('domain');

// globals
var globalDomain = domain.create();

// constants
var EVENTS = ['init', 'start', 'stop', 'finish'];
var DEFAULT_OPTIONS = {
	catchSignals: true,
	catchErrors: true,
	exitProcess: true,
	showErrors: true,
	showTraces: false,
	logTimes: true,
};

var InitSystem = function()
{
	var self = this;

	// attributes
	var callbackQueues = {};
	var flags = {};
	var standaloneCallback = null;
	var startingUp = false;
	var shuttingDown = false;
	var phase = 'pre';
	self.log = new Log('info');
	self.options = {};
	self.options.overwriteWith(DEFAULT_OPTIONS);

	// init
	startup();

	function startup()
	{
		EVENTS.forEach(function(event)
		{
			callbackQueues[event] = [];
			self[event] = getAdder(event);
		});
		events.EventEmitter.call(self);
		setImmediate(function()
		{
			setTimeout(function()
			{
				reallyStartup();
			}, 0);
		});
	}

	function getAdder(event)
	{
		return function(callback)
		{
			if (flags[event])
			{
				return manageInternalError('Could not add ' + event + ' to queue after it has run: ' + new Error().stack);
			}
			callbackQueues[event].push(callback);
		};
	}

	function reallyStartup()
	{
		if (startingUp)
		{
			return manageInternalError('Could not start up again');
		}
		startingUp = true;
		var start = Date.now();
		self.emit('startup');
		setupErrors();
		runAll('init', function(error)
		{
			if (error)
			{
				return manageInternalError('Could not run init callbacks: ' + error);
			}
			runAll('start', function(error)
			{
				if (error)
				{
					return manageInternalError('Could not run start callbacks: ' + error);
				}
				if (self.options.logTimes)
				{
					var elapsedSeconds = (Date.now() - start) / 1000;
					self.log.info('Initialization took %s seconds', elapsedSeconds.toFixed(1));
				}
				self.emit('ready');
			});
		});
	}

	function manageInternalError(error)
	{
		if (self.listeners('error').length > 0)
		{
			self.emit('error', error);
		}
		if (self.options.showErrors)
		{
			var message = error;
			if (self.options.showTraces)
			{
				message = new Error(error).stack;
			}
			self.log.error('Error in phase %s: %s', phase, message);
		}
		startShutdownByError();
	}

	function runAll(event, callback)
	{
		if (flags[event])
		{
			return callback('Callbacks for ' + event + ' already run');
		}
		self.log.debug('Running queue for %s: %s callbacks', event, callbackQueues[event]);
		phase = event;
		self.emit(event + 'ing');
		runQueue(callbackQueues[event], function(error)
		{
			if (error)
			{
				return callback(error);
			}
			flags[event] = true;
			self.emit(event + 'ed');
			return callback(null);
		});
	}

	function runQueue(queue, callback)
	{
		if (!queue.length)
		{
			return callback(null);
		}
		var current = queue.shift();
		run(current, function(error)
		{
			if (error)
			{
				return callback(error);
			}
			return runQueue(queue, callback);
		});
	}

	function run(current, callback)
	{
		self.log.debug('Running callback: %s', current);
		globalDomain.run(function()
		{
			current(callback);
		});
	}

	function setupErrors()
	{
		if (self.options.catchErrors)
		{
			globalDomain.on('error', function(error)
			{
				manageUnexpectedError('error', error);
			});
			process.on('uncaughtException', function(error)
			{
				manageUnexpectedError('uncaught exception', error);
			});
			process.on('beforeExit', function()
			{
				if (!shuttingDown)
				{
					self.shutdown();
				}
			});
			process.on('exit', function()
			{
				if (!shuttingDown && self.options.showErrors)
				{
					self.log.warning('Unexpected exit, please see documentation: https://github.com/alexfernandez/inits#unexpected-exit');
				}
			});
		}
		if (self.options.catchSignals)
		{
			process.on('SIGINT', function()
			{
				self.log.notice('User pressed control-C');
				signalled();
			});
			process.on('SIGTERM', function()
			{
				self.log.notice('Process killed');
				signalled();
			});
		}
	}

	function manageUnexpectedError(type, error)
	{
		if (!self.options.catchErrors)
		{
			return;
		}
		if (self.listeners('error').length > 0)
		{
			self.emit('error', 'Unexpected ' + type + ': ' + error);
		}
		if (self.options.showErrors)
		{
			self.log.alert('Unexpected %s: %s', type, error);
			if (error.stack)
			{
				self.log.alert(error.stack);
			}
		}
		startShutdownByError();
	}

	function startShutdownByError()
	{
		if (!shuttingDown)
		{
			return self.shutdown();
		}
		if (phase == 'stop')
		{
			self.log.debug('failed during stop; run finish callbacks');
			runAll('finish', function(error)
			{
				if (error)
				{
					return manageInternalError('Could not run finish callbacks: ' + error);
				}
			});
		}
		else
		{
			if (self.options.showErrors)
			{
				self.log.error('Error while finishing in phase %s', phase);
			}
			if (self.options.exitProcess)
			{
				process.exit(1);
			}
		}
	}

	function signalled()
	{
		if (!self.options.catchSignals)
		{
			return;
		}
		self.shutdown();
	}

	self.shutdown = function()
	{
		if (shuttingDown)
		{
			return manageInternalError('Could not shutdown again');
		}
		shuttingDown = true;
		var start = Date.now();
		self.emit('shutdown');
		runAll('stop', function(error)
		{
			if (error)
			{
				return manageInternalError('Could not run stop callbacks: ' + error);
			}
			runAll('finish', function(error)
			{
				if (error)
				{
					return manageInternalError('Could not run finish callbacks: ' + error);
				}
				if (self.options.logTimes)
				{
					var elapsedSeconds = (Date.now() - start) / 1000;
					self.log.info('Shutdown took %s seconds', elapsedSeconds.toFixed(1));
				}
				self.emit('end');
				if (self.options.exitProcess)
				{
					process.exit(0);
				}
			});
		});
	};

	self.standalone = function(callback)
	{
		if (standaloneCallback)
		{
			return manageInternalError('Already have standalone callback');
		}
		standaloneCallback = callback;
		self.on('ready', runStandalone);
	};

	function runStandalone()
	{
		setImmediate(function()
		{
			standaloneCallback(function(error)
			{
				if (error)
				{
					return manageInternalError('Could not run start callbacks: ' + error);
				}
				setImmediate(self.shutdown);
			});
		});
	}
};

util.inherits(InitSystem, events.EventEmitter);
module.exports = new InitSystem();
// exported for tests
module.exports.InitSystem = InitSystem;

<commit_msg>Show length of queue.<commit_after>'use strict';

/**
 * Init system for Node.js, main file.
 * (C) 2015 Alex Fernández.
 */

// requires
require('prototypes');
var Log = require('log');
var util = require('util');
var events = require('events');
var domain = require('domain');

// globals
var globalDomain = domain.create();

// constants
var EVENTS = ['init', 'start', 'stop', 'finish'];
var DEFAULT_OPTIONS = {
	catchSignals: true,
	catchErrors: true,
	exitProcess: true,
	showErrors: true,
	showTraces: false,
	logTimes: true,
};

var InitSystem = function()
{
	var self = this;

	// attributes
	var callbackQueues = {};
	var flags = {};
	var standaloneCallback = null;
	var startingUp = false;
	var shuttingDown = false;
	var phase = 'pre';
	self.log = new Log('info');
	self.options = {};
	self.options.overwriteWith(DEFAULT_OPTIONS);

	// init
	startup();

	function startup()
	{
		EVENTS.forEach(function(event)
		{
			callbackQueues[event] = [];
			self[event] = getAdder(event);
		});
		events.EventEmitter.call(self);
		setImmediate(function()
		{
			setTimeout(function()
			{
				reallyStartup();
			}, 0);
		});
	}

	function getAdder(event)
	{
		return function(callback)
		{
			if (flags[event])
			{
				return manageInternalError('Could not add ' + event + ' to queue after it has run: ' + new Error().stack);
			}
			callbackQueues[event].push(callback);
		};
	}

	function reallyStartup()
	{
		if (startingUp)
		{
			return manageInternalError('Could not start up again');
		}
		startingUp = true;
		var start = Date.now();
		self.emit('startup');
		setupErrors();
		runAll('init', function(error)
		{
			if (error)
			{
				return manageInternalError('Could not run init callbacks: ' + error);
			}
			runAll('start', function(error)
			{
				if (error)
				{
					return manageInternalError('Could not run start callbacks: ' + error);
				}
				if (self.options.logTimes)
				{
					var elapsedSeconds = (Date.now() - start) / 1000;
					self.log.info('Initialization took %s seconds', elapsedSeconds.toFixed(1));
				}
				self.emit('ready');
			});
		});
	}

	function manageInternalError(error)
	{
		if (self.listeners('error').length > 0)
		{
			self.emit('error', error);
		}
		if (self.options.showErrors)
		{
			var message = error;
			if (self.options.showTraces)
			{
				message = new Error(error).stack;
			}
			self.log.error('Error in phase %s: %s', phase, message);
		}
		startShutdownByError();
	}

	function runAll(event, callback)
	{
		if (flags[event])
		{
			return callback('Callbacks for ' + event + ' already run');
		}
		self.log.debug('Running queue for %s: %s callbacks', event, callbackQueues[event].length);
		phase = event;
		self.emit(event + 'ing');
		runQueue(callbackQueues[event], function(error)
		{
			if (error)
			{
				return callback(error);
			}
			flags[event] = true;
			self.emit(event + 'ed');
			return callback(null);
		});
	}

	function runQueue(queue, callback)
	{
		if (!queue.length)
		{
			return callback(null);
		}
		var current = queue.shift();
		run(current, function(error)
		{
			if (error)
			{
				return callback(error);
			}
			return runQueue(queue, callback);
		});
	}

	function run(current, callback)
	{
		self.log.debug('Running callback: %s', current);
		globalDomain.run(function()
		{
			current(callback);
		});
	}

	function setupErrors()
	{
		if (self.options.catchErrors)
		{
			globalDomain.on('error', function(error)
			{
				manageUnexpectedError('error', error);
			});
			process.on('uncaughtException', function(error)
			{
				manageUnexpectedError('uncaught exception', error);
			});
			process.on('beforeExit', function()
			{
				if (!shuttingDown)
				{
					self.shutdown();
				}
			});
			process.on('exit', function()
			{
				if (!shuttingDown && self.options.showErrors)
				{
					self.log.warning('Unexpected exit, please see documentation: https://github.com/alexfernandez/inits#unexpected-exit');
				}
			});
		}
		if (self.options.catchSignals)
		{
			process.on('SIGINT', function()
			{
				self.log.notice('User pressed control-C');
				signalled();
			});
			process.on('SIGTERM', function()
			{
				self.log.notice('Process killed');
				signalled();
			});
		}
	}

	function manageUnexpectedError(type, error)
	{
		if (!self.options.catchErrors)
		{
			return;
		}
		if (self.listeners('error').length > 0)
		{
			self.emit('error', 'Unexpected ' + type + ': ' + error);
		}
		if (self.options.showErrors)
		{
			self.log.alert('Unexpected %s: %s', type, error);
			if (error.stack)
			{
				self.log.alert(error.stack);
			}
		}
		startShutdownByError();
	}

	function startShutdownByError()
	{
		if (!shuttingDown)
		{
			return self.shutdown();
		}
		if (phase == 'stop')
		{
			self.log.debug('failed during stop; run finish callbacks');
			runAll('finish', function(error)
			{
				if (error)
				{
					return manageInternalError('Could not run finish callbacks: ' + error);
				}
			});
		}
		else
		{
			if (self.options.showErrors)
			{
				self.log.error('Error while finishing in phase %s', phase);
			}
			if (self.options.exitProcess)
			{
				process.exit(1);
			}
		}
	}

	function signalled()
	{
		if (!self.options.catchSignals)
		{
			return;
		}
		self.shutdown();
	}

	self.shutdown = function()
	{
		if (shuttingDown)
		{
			return manageInternalError('Could not shutdown again');
		}
		shuttingDown = true;
		var start = Date.now();
		self.emit('shutdown');
		runAll('stop', function(error)
		{
			if (error)
			{
				return manageInternalError('Could not run stop callbacks: ' + error);
			}
			runAll('finish', function(error)
			{
				if (error)
				{
					return manageInternalError('Could not run finish callbacks: ' + error);
				}
				if (self.options.logTimes)
				{
					var elapsedSeconds = (Date.now() - start) / 1000;
					self.log.info('Shutdown took %s seconds', elapsedSeconds.toFixed(1));
				}
				self.emit('end');
				if (self.options.exitProcess)
				{
					process.exit(0);
				}
			});
		});
	};

	self.standalone = function(callback)
	{
		if (standaloneCallback)
		{
			return manageInternalError('Already have standalone callback');
		}
		standaloneCallback = callback;
		self.on('ready', runStandalone);
	};

	function runStandalone()
	{
		setImmediate(function()
		{
			standaloneCallback(function(error)
			{
				if (error)
				{
					return manageInternalError('Could not run start callbacks: ' + error);
				}
				setImmediate(self.shutdown);
			});
		});
	}
};

util.inherits(InitSystem, events.EventEmitter);
module.exports = new InitSystem();
// exported for tests
module.exports.InitSystem = InitSystem;

<|endoftext|>


<commit_before>
/*!
 * Jade - Lexer
 * Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca>
 * MIT Licensed
 */

/**
 * Initialize `Lexer` with the given `str`.
 *
 * @param {String} str
 * @api private
 */

var Lexer = module.exports = function Lexer(str) {
    this.input = str.replace(/\r\n|\r/g, '\n').replace(/\t/g, '  ');
    this.deferredTokens = [];
    this.lastIndents = 0;
    this.lineno = 1;
};

/**
 * Lexer prototype.
 */

Lexer.prototype = {

    /**
     * Construct a token with the given `type` `line` and `val`.
     *
     * @param {String} type
     * @param {String} val
     * @param {Number} line
     * @return {Object}
     * @api private
     */

    tok: function(type, val, line){
        return {
            type: type,
            line: line === undefined ? this.lineno : line,
            val: val
        }
    },

    /**
     * Consume the given `len` of input.
     *
     * @param {Number} len
     * @api private
     */

    consume: function(len){
        this.input = this.input.substr(len);
    },

    /**
     * Single token lookahead.
     *
     * @return {Object}
     * @api private
     */

    get peek(){
        return this.stash = this.advance;
    },

    /**
     * Stashed token.
     */

    get stashed() {
        if (!this.stash) return;
        var tok = this.stash;
        delete this.stash;
        return tok;
    },

    /**
     * Deferred token.
     */

    get deferred() {
        if (!this.deferredTokens.length) return;
        return this.deferredTokens.shift();
    },

    /**
     * end-of-source.
     */

    get eos() {
        if (this.input.length) return;
        return this.lastIndents-- > 0
            ? this.tok('outdent')
            : this.tok('eos');
    },

    /**
     * Comment.
     */

    get comment() {
        var captures;
        if (captures = /^ *\/\/(-)?([^\n]+)/.exec(this.input)) {
            this.consume(captures[0].length);
            var tok = this.tok('comment', captures[2]);
            tok.buffer = captures[1] !== '-';
            return tok;
        }
    },

    /**
     * Tag.
     */

    get tag() {
        var captures;
        if (captures = /^(\w[:-\w]*)/.exec(this.input)) {
            this.consume(captures[0].length);
            return this.tok('tag', captures[1]);
        }
    },

    /**
     * Filter.
     */

    get filter() {
        var captures;
        if (captures = /^:(\w+)/.exec(this.input)) {
            this.consume(captures[0].length);
            return this.tok('filter', captures[1]);
        }
    },

    /**
     * Each.
     */

    get each() {
        var captures;
        if (captures = /^- *each *(\w+)(?: *, *(\w+))? * in *([^\n]+)/.exec(this.input)) {
            this.consume(captures[0].length);
            var tok = this.tok('each', captures[1]);
            tok.key = captures[2] || 'index';
            tok.code = captures[3];
            return tok;
        }
    },

    /**
     * Code.
     */

    get code() {
        var captures;
        if (captures = /^(!?=|-)([^\n]+)/.exec(this.input)) {
            this.consume(captures[0].length);
            var flags = captures[1];
            captures[1] = captures[2];
            var tok = this.tok('code', captures[1]);
            tok.escape = flags[0] === '=';
            tok.buffer = flags[0] === '=' || flags[1] === '=';
            return tok;
        }
    },

    /**
     * Doctype.
     */

    get doctype() {
        var captures;
        if (captures = /^!!! *(\w+)?/.exec(this.input)) {
            this.consume(captures[0].length);
            return this.tok('doctype', captures[1]);
        }
    },

    /**
     * Return the next token object.
     *
     * @return {Object}
     * @api private
     */

    get advance(){
        var self = this,
        captures;

        // stashed
        // deferred
        // eos
        // comment
        // Tag
        // Filter
        // Each
        // Code
        // Doctype

        // Id
        if (captures = /^#([\w-]+)/.exec(this.input)) {
            return token('id');
        }

        // Class
        if (captures = /^\.([\w-]+)/.exec(this.input)) {
            return token('class');
        }

        // Attributes
        if (captures = /^\((.+)\)/.exec(this.input)) {
            var tok = token('attrs'),
                attrs = tok.val.split(/ *, *(?=[\w-]+ *[:=]|[\w-]+ *$)/);
            tok.attrs = {};
            for (var i = 0, len = attrs.length; i < len; ++i) {
                var pair = attrs[i];

                // Support = and :
                var colon = pair.indexOf(':'),
                    equal = pair.indexOf('=');

                // Boolean
                if (colon < 0 && equal < 0) {
                    var key = pair,
                        val = true;
                } else {
                    // Split on first = or :
                    var split = equal >= 0
                        ? equal
                        : colon;
                    if (colon >= 0 && colon < equal) split = colon;
                    var key = pair.substr(0, split),
                        val = pair.substr(++split, pair.length);
                }
                tok.attrs[key.trim().replace(/^['"]|['"]$/g, '')] = val;
            }
            return tok;
        }

        // Indent
        if (captures = /^\n( *)/.exec(this.input)) {
            ++this.lineno;
            var tok = token('indent'),
                indents = tok.val.length / 2;
            if (this.input[0] === '\n') {
                tok.type = 'newline';
                return tok;
            } else if (indents % 1 !== 0) {
                throw new Error('Invalid indentation, got '
                    + tok.val.length + ' space'
                    + (tok.val.length > 1 ? 's' : '')
                    + ', must be a multiple of two.');
            } else if (indents === this.lastIndents) {
                tok.type = 'newline';
            } else if (indents > this.lastIndents + 1) {
                throw new Error('Invalid indentation, got '
                    + indents + ' expected '
                    + (this.lastIndents + 1) + '.');
            } else if (indents < this.lastIndents) {
                var n = this.lastIndents - indents;
                tok.type = 'outdent';
                while (--n) {
                    this.deferredTokens.push({
                        type: 'outdent',
                        line: this.lineno
                    });
                }
            }
            this.lastIndents = indents;
            return tok;
        }

        // Text
        if (captures = /^(?:\| ?)?([^\n]+)/.exec(this.input)) {
            return token('text');
        }
    }
};
<commit_msg>Added Lexer#class<commit_after>
/*!
 * Jade - Lexer
 * Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca>
 * MIT Licensed
 */

/**
 * Initialize `Lexer` with the given `str`.
 *
 * @param {String} str
 * @api private
 */

var Lexer = module.exports = function Lexer(str) {
    this.input = str.replace(/\r\n|\r/g, '\n').replace(/\t/g, '  ');
    this.deferredTokens = [];
    this.lastIndents = 0;
    this.lineno = 1;
};

/**
 * Lexer prototype.
 */

Lexer.prototype = {

    /**
     * Construct a token with the given `type` `line` and `val`.
     *
     * @param {String} type
     * @param {String} val
     * @param {Number} line
     * @return {Object}
     * @api private
     */

    tok: function(type, val, line){
        return {
            type: type,
            line: line === undefined ? this.lineno : line,
            val: val
        }
    },

    /**
     * Consume the given `len` of input.
     *
     * @param {Number} len
     * @api private
     */

    consume: function(len){
        this.input = this.input.substr(len);
    },

    /**
     * Single token lookahead.
     *
     * @return {Object}
     * @api private
     */

    get peek(){
        return this.stash = this.advance;
    },

    /**
     * Stashed token.
     */

    get stashed() {
        if (!this.stash) return;
        var tok = this.stash;
        delete this.stash;
        return tok;
    },

    /**
     * Deferred token.
     */

    get deferred() {
        if (!this.deferredTokens.length) return;
        return this.deferredTokens.shift();
    },

    /**
     * end-of-source.
     */

    get eos() {
        if (this.input.length) return;
        return this.lastIndents-- > 0
            ? this.tok('outdent')
            : this.tok('eos');
    },

    /**
     * Comment.
     */

    get comment() {
        var captures;
        if (captures = /^ *\/\/(-)?([^\n]+)/.exec(this.input)) {
            this.consume(captures[0].length);
            var tok = this.tok('comment', captures[2]);
            tok.buffer = captures[1] !== '-';
            return tok;
        }
    },

    /**
     * Tag.
     */

    get tag() {
        var captures;
        if (captures = /^(\w[:-\w]*)/.exec(this.input)) {
            this.consume(captures[0].length);
            return this.tok('tag', captures[1]);
        }
    },

    /**
     * Filter.
     */

    get filter() {
        var captures;
        if (captures = /^:(\w+)/.exec(this.input)) {
            this.consume(captures[0].length);
            return this.tok('filter', captures[1]);
        }
    },

    /**
     * Each.
     */

    get each() {
        var captures;
        if (captures = /^- *each *(\w+)(?: *, *(\w+))? * in *([^\n]+)/.exec(this.input)) {
            this.consume(captures[0].length);
            var tok = this.tok('each', captures[1]);
            tok.key = captures[2] || 'index';
            tok.code = captures[3];
            return tok;
        }
    },

    /**
     * Code.
     */

    get code() {
        var captures;
        if (captures = /^(!?=|-)([^\n]+)/.exec(this.input)) {
            this.consume(captures[0].length);
            var flags = captures[1];
            captures[1] = captures[2];
            var tok = this.tok('code', captures[1]);
            tok.escape = flags[0] === '=';
            tok.buffer = flags[0] === '=' || flags[1] === '=';
            return tok;
        }
    },

    /**
     * Doctype.
     */

    get doctype() {
        var captures;
        if (captures = /^!!! *(\w+)?/.exec(this.input)) {
            this.consume(captures[0].length);
            return this.tok('doctype', captures[1]);
        }
    },

    /**
     * Id.
     */

    get id() {
        var captures;
        if (captures = /^#([\w-]+)/.exec(this.input)) {
            this.consume(captures[0].length);
            return this.tok('id', captures[1]);
        }
    },

    /**
     * Class.
     */

    get class() {
        var captures;
        if (captures = /^\.([\w-]+)/.exec(this.input)) {
            this.consume(captures[0].length);
            return this.tok('class' captures[1]);
        }
    }

    /**
     * Return the next token object.
     *
     * @return {Object}
     * @api private
     */

    get advance(){
        var self = this,
        captures;

        // stashed
        // deferred
        // eos
        // comment
        // Tag
        // Filter
        // Each
        // Code
        // Doctype
        // Id
        // Class

        // Attributes
        if (captures = /^\((.+)\)/.exec(this.input)) {
            var tok = token('attrs'),
                attrs = tok.val.split(/ *, *(?=[\w-]+ *[:=]|[\w-]+ *$)/);
            tok.attrs = {};
            for (var i = 0, len = attrs.length; i < len; ++i) {
                var pair = attrs[i];

                // Support = and :
                var colon = pair.indexOf(':'),
                    equal = pair.indexOf('=');

                // Boolean
                if (colon < 0 && equal < 0) {
                    var key = pair,
                        val = true;
                } else {
                    // Split on first = or :
                    var split = equal >= 0
                        ? equal
                        : colon;
                    if (colon >= 0 && colon < equal) split = colon;
                    var key = pair.substr(0, split),
                        val = pair.substr(++split, pair.length);
                }
                tok.attrs[key.trim().replace(/^['"]|['"]$/g, '')] = val;
            }
            return tok;
        }

        // Indent
        if (captures = /^\n( *)/.exec(this.input)) {
            ++this.lineno;
            var tok = token('indent'),
                indents = tok.val.length / 2;
            if (this.input[0] === '\n') {
                tok.type = 'newline';
                return tok;
            } else if (indents % 1 !== 0) {
                throw new Error('Invalid indentation, got '
                    + tok.val.length + ' space'
                    + (tok.val.length > 1 ? 's' : '')
                    + ', must be a multiple of two.');
            } else if (indents === this.lastIndents) {
                tok.type = 'newline';
            } else if (indents > this.lastIndents + 1) {
                throw new Error('Invalid indentation, got '
                    + indents + ' expected '
                    + (this.lastIndents + 1) + '.');
            } else if (indents < this.lastIndents) {
                var n = this.lastIndents - indents;
                tok.type = 'outdent';
                while (--n) {
                    this.deferredTokens.push({
                        type: 'outdent',
                        line: this.lineno
                    });
                }
            }
            this.lastIndents = indents;
            return tok;
        }

        // Text
        if (captures = /^(?:\| ?)?([^\n]+)/.exec(this.input)) {
            return token('text');
        }
    }
};
<|endoftext|>


<commit_before>
/*!
 * Jade - Lexer
 * Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca>
 * MIT Licensed
 */

/**
 * Initialize `Lexer` with the given `str`.
 *
 * @param {String} str
 * @api private
 */

var Lexer = module.exports = function Lexer(str) {
    this.input = str.replace(/\r\n|\r/g, '\n').replace(/\t/g, '  ');
    this.deferredTokens = [];
    this.lastIndents = 0;
    this.lineno = 1;
};

/**
 * Lexer prototype.
 */

Lexer.prototype = {

    /**
     * Construct a token with the given `type` `line` and `val`.
     *
     * @param {String} type
     * @param {String} val
     * @param {Number} line
     * @return {Object}
     * @api private
     */

    tok: function(type, val, line){
        return {
            type: type,
            line: line === undefined ? this.lineno : line,
            val: val
        }
    },

    /**
     * Consume the given `len` of input.
     *
     * @param {Number} len
     * @api private
     */

    consume: function(len){
        this.input = this.input.substr(len);
    },

    /**
     * Single token lookahead.
     *
     * @return {Object}
     * @api private
     */

    get peek(){
        return this.stash = this.advance;
    },

    /**
     * Stashed token.
     */

    get stashed() {
        if (!this.stash) return;
        var tok = this.stash;
        delete this.stash;
        return tok;
    },

    /**
     * Deferred token.
     */

    get deferred() {
        if (!this.deferredTokens.length) return;
        return this.deferredTokens.shift();
    },

    /**
     * end-of-source.
     */

    get eos() {
        if (this.input.length) return;
        return this.lastIndents-- > 0
            ? this.tok('outdent')
            : this.tok('eos');
    },

    /**
     * Comment.
     */

    get comment() {
        var captures;
        if (captures = /^ *\/\/(-)?([^\n]+)/.exec(this.input)) {
            this.consume(captures[0].length);
            var tok = this.tok('comment', captures[2]);
            tok.buffer = captures[1] !== '-';
            return tok;
        }
    },

    /**
     * Tag.
     */

    get tag() {
        var captures;
        if (captures = /^(\w[:-\w]*)/.exec(this.input)) {
            this.consume(captures[0].length);
            return this.tok('tag', captures[1]);
        }
    },

    /**
     * Filter.
     */

    get filter() {
        var captures;
        if (captures = /^:(\w+)/.exec(this.input)) {
            this.consume(captures[0].length);
            return this.tok('filter', captures[1]);
        }
    },

    /**
     * Each.
     */

    get each() {
        var captures;
        if (captures = /^- *each *(\w+)(?: *, *(\w+))? * in *([^\n]+)/.exec(this.input)) {
            this.consume(captures[0].length);
            var tok = this.tok('each', captures[1]);
            tok.key = captures[2] || 'index';
            tok.code = captures[3];
            return tok;
        }
    },

    /**
     * Code.
     */

    get code() {
        var captures;
        if (captures = /^(!?=|-)([^\n]+)/.exec(this.input)) {
            this.consume(captures[0].length);
            var flags = captures[1];
            captures[1] = captures[2];
            var tok = this.tok('code', captures[1]);
            tok.escape = flags[0] === '=';
            tok.buffer = flags[0] === '=' || flags[1] === '=';
            return tok;
        }
    },

    /**
     * Doctype.
     */

    get doctype() {
        var captures;
        if (captures = /^!!! *(\w+)?/.exec(this.input)) {
            this.consume(captures[0].length);
            return this.tok('doctype', captures[1]);
        }
    },

    /**
     * Id.
     */

    get id() {
        var captures;
        if (captures = /^#([\w-]+)/.exec(this.input)) {
            this.consume(captures[0].length);
            return this.tok('id', captures[1]);
        }
    },

    /**
     * Class.
     */

    get className() {
        var captures;
        if (captures = /^\.([\w-]+)/.exec(this.input)) {
            this.consume(captures[0].length);
            return this.tok('class' captures[1]);
        }
    },

    /**
     * Attributes.
     */

    get attrs() {
        var captures;
        if (captures = /^\((.+)\)/.exec(this.input)) {
            this.consume(captures[0].length);
            var tok = this.tok('attrs', captures[1]),
                attrs = tok.val.split(/ *, *(?=[\w-]+ *[:=]|[\w-]+ *$)/);
            tok.attrs = {};
            for (var i = 0, len = attrs.length; i < len; ++i) {
                var pair = attrs[i];

                // Support = and :
                var colon = pair.indexOf(':'),
                    equal = pair.indexOf('=');

                // Boolean
                if (colon < 0 && equal < 0) {
                    var key = pair,
                        val = true;
                } else {
                    // Split on first = or :
                    var split = equal >= 0
                        ? equal
                        : colon;
                    if (colon >= 0 && colon < equal) split = colon;
                    var key = pair.substr(0, split),
                        val = pair.substr(++split, pair.length);
                }
                tok.attrs[key.trim().replace(/^['"]|['"]$/g, '')] = val;
            }
            return tok;
        }
    },

    /**
     * Return the next token object.
     *
     * @return {Object}
     * @api private
     */

    get advance(){
        var self = this,
        captures;

        // stashed
        // deferred
        // eos
        // comment
        // Tag
        // Filter
        // Each
        // Code
        // Doctype
        // Id
        // Class
        // Attributes

        // Indent
        if (captures = /^\n( *)/.exec(this.input)) {
            ++this.lineno;
            var tok = token('indent'),
                indents = tok.val.length / 2;
            if (this.input[0] === '\n') {
                tok.type = 'newline';
                return tok;
            } else if (indents % 1 !== 0) {
                throw new Error('Invalid indentation, got '
                    + tok.val.length + ' space'
                    + (tok.val.length > 1 ? 's' : '')
                    + ', must be a multiple of two.');
            } else if (indents === this.lastIndents) {
                tok.type = 'newline';
            } else if (indents > this.lastIndents + 1) {
                throw new Error('Invalid indentation, got '
                    + indents + ' expected '
                    + (this.lastIndents + 1) + '.');
            } else if (indents < this.lastIndents) {
                var n = this.lastIndents - indents;
                tok.type = 'outdent';
                while (--n) {
                    this.deferredTokens.push({
                        type: 'outdent',
                        line: this.lineno
                    });
                }
            }
            this.lastIndents = indents;
            return tok;
        }

        // Text
        if (captures = /^(?:\| ?)?([^\n]+)/.exec(this.input)) {
            return token('text');
        }
    }
};
<commit_msg>Added Lexer#text<commit_after>
/*!
 * Jade - Lexer
 * Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca>
 * MIT Licensed
 */

/**
 * Initialize `Lexer` with the given `str`.
 *
 * @param {String} str
 * @api private
 */

var Lexer = module.exports = function Lexer(str) {
    this.input = str.replace(/\r\n|\r/g, '\n').replace(/\t/g, '  ');
    this.deferredTokens = [];
    this.lastIndents = 0;
    this.lineno = 1;
};

/**
 * Lexer prototype.
 */

Lexer.prototype = {

    /**
     * Construct a token with the given `type` `line` and `val`.
     *
     * @param {String} type
     * @param {String} val
     * @param {Number} line
     * @return {Object}
     * @api private
     */

    tok: function(type, val, line){
        return {
            type: type,
            line: line === undefined ? this.lineno : line,
            val: val
        }
    },

    /**
     * Consume the given `len` of input.
     *
     * @param {Number} len
     * @api private
     */

    consume: function(len){
        this.input = this.input.substr(len);
    },

    /**
     * Single token lookahead.
     *
     * @return {Object}
     * @api private
     */

    get peek(){
        return this.stash = this.advance;
    },

    /**
     * Stashed token.
     */

    get stashed() {
        if (!this.stash) return;
        var tok = this.stash;
        delete this.stash;
        return tok;
    },

    /**
     * Deferred token.
     */

    get deferred() {
        if (!this.deferredTokens.length) return;
        return this.deferredTokens.shift();
    },

    /**
     * end-of-source.
     */

    get eos() {
        if (this.input.length) return;
        return this.lastIndents-- > 0
            ? this.tok('outdent')
            : this.tok('eos');
    },

    /**
     * Comment.
     */

    get comment() {
        var captures;
        if (captures = /^ *\/\/(-)?([^\n]+)/.exec(this.input)) {
            this.consume(captures[0].length);
            var tok = this.tok('comment', captures[2]);
            tok.buffer = captures[1] !== '-';
            return tok;
        }
    },

    /**
     * Tag.
     */

    get tag() {
        var captures;
        if (captures = /^(\w[:-\w]*)/.exec(this.input)) {
            this.consume(captures[0].length);
            return this.tok('tag', captures[1]);
        }
    },

    /**
     * Filter.
     */

    get filter() {
        var captures;
        if (captures = /^:(\w+)/.exec(this.input)) {
            this.consume(captures[0].length);
            return this.tok('filter', captures[1]);
        }
    },

    /**
     * Each.
     */

    get each() {
        var captures;
        if (captures = /^- *each *(\w+)(?: *, *(\w+))? * in *([^\n]+)/.exec(this.input)) {
            this.consume(captures[0].length);
            var tok = this.tok('each', captures[1]);
            tok.key = captures[2] || 'index';
            tok.code = captures[3];
            return tok;
        }
    },

    /**
     * Code.
     */

    get code() {
        var captures;
        if (captures = /^(!?=|-)([^\n]+)/.exec(this.input)) {
            this.consume(captures[0].length);
            var flags = captures[1];
            captures[1] = captures[2];
            var tok = this.tok('code', captures[1]);
            tok.escape = flags[0] === '=';
            tok.buffer = flags[0] === '=' || flags[1] === '=';
            return tok;
        }
    },

    /**
     * Doctype.
     */

    get doctype() {
        var captures;
        if (captures = /^!!! *(\w+)?/.exec(this.input)) {
            this.consume(captures[0].length);
            return this.tok('doctype', captures[1]);
        }
    },

    /**
     * Id.
     */

    get id() {
        var captures;
        if (captures = /^#([\w-]+)/.exec(this.input)) {
            this.consume(captures[0].length);
            return this.tok('id', captures[1]);
        }
    },

    /**
     * Class.
     */

    get className() {
        var captures;
        if (captures = /^\.([\w-]+)/.exec(this.input)) {
            this.consume(captures[0].length);
            return this.tok('class' captures[1]);
        }
    },

    /**
     * Attributes.
     */

    get attrs() {
        var captures;
        if (captures = /^\((.+)\)/.exec(this.input)) {
            this.consume(captures[0].length);
            var tok = this.tok('attrs', captures[1]),
                attrs = tok.val.split(/ *, *(?=[\w-]+ *[:=]|[\w-]+ *$)/);
            tok.attrs = {};
            for (var i = 0, len = attrs.length; i < len; ++i) {
                var pair = attrs[i];

                // Support = and :
                var colon = pair.indexOf(':'),
                    equal = pair.indexOf('=');

                // Boolean
                if (colon < 0 && equal < 0) {
                    var key = pair,
                        val = true;
                } else {
                    // Split on first = or :
                    var split = equal >= 0
                        ? equal
                        : colon;
                    if (colon >= 0 && colon < equal) split = colon;
                    var key = pair.substr(0, split),
                        val = pair.substr(++split, pair.length);
                }
                tok.attrs[key.trim().replace(/^['"]|['"]$/g, '')] = val;
            }
            return tok;
        }
    },

    /**
     * Indent.
     */

    get indent() {
        var captures;
        if (captures = /^\n( *)/.exec(this.input)) {
            ++this.lineno;
            this.consume(this.captures[0].length);
            var tok = this.tok('indent', captures[1]),
                indents = tok.val.length / 2;
            if (this.input[0] === '\n') {
                tok.type = 'newline';
                return tok;
            } else if (indents % 1 !== 0) {
                throw new Error('Invalid indentation, got '
                    + tok.val.length + ' space'
                    + (tok.val.length > 1 ? 's' : '')
                    + ', must be a multiple of two.');
            } else if (indents === this.lastIndents) {
                tok.type = 'newline';
            } else if (indents > this.lastIndents + 1) {
                throw new Error('Invalid indentation, got '
                    + indents + ' expected '
                    + (this.lastIndents + 1) + '.');
            } else if (indents < this.lastIndents) {
                var n = this.lastIndents - indents;
                tok.type = 'outdent';
                while (--n) {
                    this.deferredTokens.push({
                        type: 'outdent',
                        line: this.lineno
                    });
                }
            }
            this.lastIndents = indents;
            return tok;
        }
    },

    /**
     * Text.
     */

    get text() {
        var captures;
        if (captures = /^(?:\| ?)?([^\n]+)/.exec(this.input)) {
            this.consume(captures[0].length);
            return this.tok('text', captures[1]);
        }
    },

    /**
     * Return the next token object.
     *
     * @return {Object}
     * @api private
     */

    get advance(){
        var self = this,
        captures;

        // stashed
        // deferred
        // eos
        // comment
        // Tag
        // Filter
        // Each
        // Code
        // Doctype
        // Id
        // Class
        // Attributes
        // Indent

        // Text

    }
};
<|endoftext|>