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

/**
 * Module dependencies.
 */

var sys = require('sys'),
    url = require('url'),
    view = require('./view'),
    connect = require('connect'),
    utils = require('connect/utils'),
    queryString = require('querystring'),
    router = require('connect/middleware/router');

/**
 * Initialize a new `Server` with optional `middleware`.
 *
 * @param {Array} middleware
 * @api public
 */

var Server = exports = module.exports = function Server(middleware){
    var self = this;
    this.config = {};
    this.settings = {};
    this.redirects = {};
    this.viewHelpers = {};
    this.dynamicViewHelpers = {};
    connect.Server.call(this, middleware || []);

    // Default "home" to /
    this.set('home', '/');

    // Set "env" to {EXPRESS,CONNECT}_ENV
    this.set('env', process.env.EXPRESS_ENV || process.connectEnv.name);

    // Expose objects to each other
    this.use(function(req, res, next){
        req.params = req.params || {};
        req.params.get = {};
        res.headers = {};
        req.app = res.app = self;
        req.res = res;
        res.req = req;
        req.next = next;
        // Assign req.params.get
        if (req.url.indexOf('?') > 0) {
            // TODO: consider simple substr to increase performance
            var query = url.parse(req.url).query;
            req.params.get = queryString.parse(query);
        }
        next();
    });

    // Use router, expose as app.get(), etc
    var fn = router(function(app){ self.routes = app; });
    this.__defineGetter__('router', function(){
        this.__usedRouter = true;
        return fn;
    });
};

/**
 * Inherit from `connect.Server`.
 */

sys.inherits(Server, connect.Server);

/**
 * Start listening on the given `port` / `host`.
 *
 * @param {Number} port
 * @param {String} host
 * @api public
 */

Server.prototype.listen = function(port, host){
    // Setup view reloading
    if (this.set('reload views')) {
        view.watcher.call(this, this.set('reload views'));
    }
    connect.Server.prototype.listen.call(this, port, host);
};

/**
 * Proxy `connect.Server#use()` to apply settings to
 * mounted applications.
 *
 * @param {String|Function|Server} route
 * @param {Function|Server} middleware
 * @return {Server} for chaining
 * @api public
 */

Server.prototype.use = function(route, middleware){
    if (typeof route !== 'string') {
        middleware = route, route = '/';
    }

    connect.Server.prototype.use.call(this, route, middleware);

    // Mounted an app
    if (middleware instanceof Server) {
        // Home is /:route/:home
        var app = middleware,
            home = app.set('home');
        if (home === '/') home = '';
        app.set('home', (app.route || '') + home);
        // Mounted hook
        if (app.__mounted) app.__mounted.call(app, this);
    }

    return this;
};

Server.prototype.mounted = function(fn){
    this.__mounted = fn;
};

/**
 * Register the given view helpers `obj`. This method
 * can be called several times to apply additional helpers.
 *
 * @param {Object} obj
 * @return {Server} for chaining
 * @api public
 */

Server.prototype.helpers = function(obj){
    utils.merge(this.viewHelpers, obj);
    return this;
};

/**
 * Register the given dynamic view helpers `obj`. This method
 * can be called several times to apply additional helpers.
 *
 * @param {Object} obj
 * @return {Server} for chaining
 * @api public
 */

Server.prototype.dynamicHelpers = function(obj){
    utils.merge(this.dynamicViewHelpers, obj);
    return this;
};

/**
 * Assign a custom exception handler callback `fn`.
 *
 * @param {Function} fn
 * @return {Server} for chaining
 * @api public
 */

Server.prototype.error = function(fn){
    this.use(function(err, req, res, next){
        fn.apply(this, arguments);
    });
    return this;
};

/**
 * Assign `setting` to `val`, or return `setting`'s value.
 *
 * @param {String} setting
 * @param {String} val
 * @return {Server|Mixed} for chaining, or the setting value
 * @api public
 */

Server.prototype.set = function(setting, val){
    if (val === undefined) {
        return this.settings[setting];
    } else {
        this.settings[setting] = val;
        return this;
    }
};

/**
 * Enable `setting`.
 *
 * @param {String} setting
 * @return {Server} for chaining
 * @api public
 */

Server.prototype.enable = function(setting){
    return this.set(setting, true);
};

/**
 * Disable `setting`.
 *
 * @param {String} setting
 * @return {Server} for chaining
 * @api public
 */

Server.prototype.disable = function(setting){
    return this.set(setting, false);
};

/**
 * Redirect `key` to `url`.
 *
 * @param {String} key
 * @param {String} url
 * @return {Server} for chaining
 * @api public
 */

Server.prototype.redirect = function(key, url){
    this.redirects[key] = url;
    return this;
};

/**
 * Configure callback for the given `env`.
 *
 * @param {String} env
 * @param {Function} fn
 * @return {Server} for chaining
 * @api public
 */

Server.prototype.configure = function(env, fn){
    if (typeof env === 'function') {
        fn = env, env = 'all';
    }
    if (env === 'all' || this.set('env') === env) {
        fn.call(this);
    }
    return this;
};

// Generate routing methods

(function(method){
    Server.prototype[method] = function(path, fn){
        if (!this.__usedRouter) {
            this.use(this.router);
        }
        this.routes[method](path, fn);
        return this;
    };
    return arguments.callee;
})('get')('post')('put')('del');
<commit_msg>Added docs for Server#mounted()<commit_after>
/*!
 * Express - Server
 * Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca>
 * MIT Licensed
 */

/**
 * Module dependencies.
 */

var sys = require('sys'),
    url = require('url'),
    view = require('./view'),
    connect = require('connect'),
    utils = require('connect/utils'),
    queryString = require('querystring'),
    router = require('connect/middleware/router');

/**
 * Initialize a new `Server` with optional `middleware`.
 *
 * @param {Array} middleware
 * @api public
 */

var Server = exports = module.exports = function Server(middleware){
    var self = this;
    this.config = {};
    this.settings = {};
    this.redirects = {};
    this.viewHelpers = {};
    this.dynamicViewHelpers = {};
    connect.Server.call(this, middleware || []);

    // Default "home" to /
    this.set('home', '/');

    // Set "env" to {EXPRESS,CONNECT}_ENV
    this.set('env', process.env.EXPRESS_ENV || process.connectEnv.name);

    // Expose objects to each other
    this.use(function(req, res, next){
        req.params = req.params || {};
        req.params.get = {};
        res.headers = {};
        req.app = res.app = self;
        req.res = res;
        res.req = req;
        req.next = next;
        // Assign req.params.get
        if (req.url.indexOf('?') > 0) {
            // TODO: consider simple substr to increase performance
            var query = url.parse(req.url).query;
            req.params.get = queryString.parse(query);
        }
        next();
    });

    // Use router, expose as app.get(), etc
    var fn = router(function(app){ self.routes = app; });
    this.__defineGetter__('router', function(){
        this.__usedRouter = true;
        return fn;
    });
};

/**
 * Inherit from `connect.Server`.
 */

sys.inherits(Server, connect.Server);

/**
 * Start listening on the given `port` / `host`.
 *
 * @param {Number} port
 * @param {String} host
 * @api public
 */

Server.prototype.listen = function(port, host){
    // Setup view reloading
    if (this.set('reload views')) {
        view.watcher.call(this, this.set('reload views'));
    }
    connect.Server.prototype.listen.call(this, port, host);
};

/**
 * Proxy `connect.Server#use()` to apply settings to
 * mounted applications.
 *
 * @param {String|Function|Server} route
 * @param {Function|Server} middleware
 * @return {Server} for chaining
 * @api public
 */

Server.prototype.use = function(route, middleware){
    if (typeof route !== 'string') {
        middleware = route, route = '/';
    }

    connect.Server.prototype.use.call(this, route, middleware);

    // Mounted an app
    if (middleware instanceof Server) {
        // Home is /:route/:home
        var app = middleware,
            home = app.set('home');
        if (home === '/') home = '';
        app.set('home', (app.route || '') + home);
        // Mounted hook
        if (app.__mounted) app.__mounted.call(app, this);
    }

    return this;
};

/**
 * Assign a callback `fn` which is called
 * when this `Server` is passed to `Server#use()`.
 *
 * Examples:
 *
 *    var app = express.createServer(),
 *        blog = express.createServer();
 *
 *    blog.mounted(function(parent){
 *        // parent is app
 *        // "this" is blog
 *    });
 *
 *    app.use(blog);
 *
 * @param {Function} fn
 * @return {Server} for chaining
 * @api public
 */

Server.prototype.mounted = function(fn){
    this.__mounted = fn;
    return this;
};

/**
 * Register the given view helpers `obj`. This method
 * can be called several times to apply additional helpers.
 *
 * @param {Object} obj
 * @return {Server} for chaining
 * @api public
 */

Server.prototype.helpers = function(obj){
    utils.merge(this.viewHelpers, obj);
    return this;
};

/**
 * Register the given dynamic view helpers `obj`. This method
 * can be called several times to apply additional helpers.
 *
 * @param {Object} obj
 * @return {Server} for chaining
 * @api public
 */

Server.prototype.dynamicHelpers = function(obj){
    utils.merge(this.dynamicViewHelpers, obj);
    return this;
};

/**
 * Assign a custom exception handler callback `fn`.
 *
 * @param {Function} fn
 * @return {Server} for chaining
 * @api public
 */

Server.prototype.error = function(fn){
    this.use(function(err, req, res, next){
        fn.apply(this, arguments);
    });
    return this;
};

/**
 * Assign `setting` to `val`, or return `setting`'s value.
 *
 * @param {String} setting
 * @param {String} val
 * @return {Server|Mixed} for chaining, or the setting value
 * @api public
 */

Server.prototype.set = function(setting, val){
    if (val === undefined) {
        return this.settings[setting];
    } else {
        this.settings[setting] = val;
        return this;
    }
};

/**
 * Enable `setting`.
 *
 * @param {String} setting
 * @return {Server} for chaining
 * @api public
 */

Server.prototype.enable = function(setting){
    return this.set(setting, true);
};

/**
 * Disable `setting`.
 *
 * @param {String} setting
 * @return {Server} for chaining
 * @api public
 */

Server.prototype.disable = function(setting){
    return this.set(setting, false);
};

/**
 * Redirect `key` to `url`.
 *
 * @param {String} key
 * @param {String} url
 * @return {Server} for chaining
 * @api public
 */

Server.prototype.redirect = function(key, url){
    this.redirects[key] = url;
    return this;
};

/**
 * Configure callback for the given `env`.
 *
 * @param {String} env
 * @param {Function} fn
 * @return {Server} for chaining
 * @api public
 */

Server.prototype.configure = function(env, fn){
    if (typeof env === 'function') {
        fn = env, env = 'all';
    }
    if (env === 'all' || this.set('env') === env) {
        fn.call(this);
    }
    return this;
};

// Generate routing methods

(function(method){
    Server.prototype[method] = function(path, fn){
        if (!this.__usedRouter) {
            this.use(this.router);
        }
        this.routes[method](path, fn);
        return this;
    };
    return arguments.callee;
})('get')('post')('put')('del');
<|endoftext|>


<commit_before>const helper = require('think-helper');
const assert = require('assert');
const Cookies = require('cookies');
const url = require('url');

const PARAM = Symbol('context-param');
const POST = Symbol('context-post');
const FILE = Symbol('context-file');
const COOKIE_STORE = Symbol('cookie-store');
const path = require('path');
const fs = require('fs');
const onFinished = require('on-finished');
const destroy = require('destroy');

/**
 * extend context
 */
module.exports = {
  /**
   * get userAgent header
   */
  get userAgent() {
    return this.header['user-agent'];
  },
  /**
   * get referer header
   */
  referer(onlyHost) {
    return this.referrer(onlyHost);
  },
  /**
   * get referer header
   */
  referrer(onlyHost) {
    const referrer = this.header['referer'];
    if (!referrer || !onlyHost) return referrer;
    return url.parse(referrer).hostname;
  },
  /**
   * is get request
   */
  get isGet() {
    return this.method === 'GET';
  },
  /**
   * is post request
   */
  get isPost() {
    return this.method === 'POST';
  },
  /**
   * is method
   */
  isMethod(method) {
    return this.method === method.toUpperCase();
  },
  /**
   * is command line invoke
   */
  get isCli() {
    return think.isCli;
  },
  /**
   * is ajax request
   */
  isAjax(method) {
    if (method && !this.isMethod(method)) return false;
    return this.header['x-requested-with'] === 'XMLHttpRequest';
  },
  /**
   * is jsonp request
   */
  isJsonp(callbackField = this.config('jsonpCallbackField')) {
    return !!this.param(callbackField);
  },
  /**
   * send jsonp data
   */
  jsonp(data, callbackField = this.config('jsonpCallbackField')) {
    let field = this.param(callbackField);
    // remove unsafe chars
    field = (field || '').replace(/[^\w.]/g, '');
    if (field) {
      data = `${field}(${JSON.stringify(data)})`;
    }
    this.type = this.config('jsonContentType');
    this.body = data;
    return false;
  },
  /**
   * send json data
   */
  json(data) {
    this.type = this.config('jsonContentType');
    this.body = data;
    return false;
  },
  /**
   * send success data
   */
  success(data = '', message = '') {
    const obj = {
      [this.config('errnoField')]: 0,
      [this.config('errmsgField')]: message,
      data
    };
    this.type = this.config('jsonContentType');
    this.body = obj;
    return false;
  },
  /**
   * send fail data
   */
  fail(errno, errmsg = '', data = '') {
    let obj;
    if (helper.isObject(errno)) {
      obj = errno;
    } else {
      if (/^[A-Z_]+$/.test(errno)) {
        const messages = think.app.validators.messages || {};
        const msg = messages[errno];
        if (think.isArray(msg)) {
          [errno, errmsg] = msg;
        }
      }
      if (!think.isNumber(errno)) {
        [data, errmsg, errno] = [errmsg, errno, this.config('defaultErrno')];
      }
      obj = {
        [this.config('errnoField')]: errno,
        [this.config('errmsgField')]: errmsg
      };
      if (data) {
        obj.data = data;
      }
    }
    this.type = this.config('jsonContentType');
    this.body = obj;
    return false;
  },
  /**
   * set expires header
   */
  expires(time) {
    time = helper.ms(time);
    const date = new Date(Date.now() + time);
    this.set('Cache-Control', `max-age=${time}`);
    this.set('Expires', date.toUTCString());
  },
  /**
   * get or set configs
   * @param {String} name
   * @param {Mixed} value
   * @param {String} m
   */
  config(name, value, m = this.module) {
    return think.config(name, value, m);
  },
  /**
   * get or set query data
   * `query` or `get` is already used in koa
   * @param {String} name
   * @param {Mixed} value
   */
  param(name, value) {
    if (!this[PARAM]) {
      this[PARAM] = Object.assign({}, this.query);
    }
    if (!name) return this[PARAM];
    if (helper.isObject(name)) {
      this[PARAM] = Object.assign(this[PARAM], name);
      return this;
    }
    assert(name && helper.isString(name), 'param.name must be a string');
    if (value === undefined) {
      // this.param('a,b')
      if (name.indexOf(',') > -1) {
        const value = {};
        name.split(',').forEach(item => {
          value[item] = this[PARAM][item];
        });
        return value;
      }
      return this[PARAM][name];
    }
    this[PARAM][name] = value;
    return this;
  },
  /**
   * get or set post data
   * @param {String} name
   * @param {Mixed} value
   */
  post(name, value) {
    if (!this[POST]) {
      this[POST] = Object.assign({}, this.request.body && this.request.body.post);
    }
    if (!name) return this[POST];
    if (helper.isObject(name)) {
      this[POST] = Object.assign(this[POST], name);
      return this;
    }
    if (value === undefined) {
      // this.param('a,b')
      if (name.indexOf(',') > -1) {
        const value = {};
        name.split(',').forEach(item => {
          value[item] = this[POST][item];
        });
        return value;
      }
      return this[POST][name];
    }
    this[POST][name] = value;
    return this;
  },
  /**
   * get or set file data
   * @param {String} name
   * @param {Mixed} value
   */
  file(name, value) {
    if (!this[FILE]) {
      this[FILE] = Object.assign({}, this.request.body && this.request.body.file);
    }
    if (!name) return this[FILE];
    if (helper.isObject(name)) {
      this[FILE] = Object.assign(this[FILE], name);
      return this;
    }
    if (value === undefined) {
      return this[FILE][name];
    }
    this[FILE][name] = value;
    return this;
  },
  /**
   * get or set cookie
   * @param {String} name
   * @param {String} value
   * @param {Object} options
   */
  cookie(name, value, options = {}) {
    assert(name && helper.isString(name), 'cookie.name must be a string');
    options = Object.assign({}, this.config('cookie'), options);
    const instance = new Cookies(this.req, this.res, {
      keys: options.keys,
      secure: this.req.secure
    });

    if (!this[COOKIE_STORE]) this[COOKIE_STORE] = {};

    // get cookie
    if (value === undefined) {
      if (this[COOKIE_STORE][name] !== undefined) return this[COOKIE_STORE][name];
      return instance.get(name, options);
    }
    // remove cookie
    if (value === null) {
      delete this[COOKIE_STORE][name];
      // If the value is omitted, an outbound header with an expired date is used to delete the cookie.
      // https://github.com/pillarjs/cookies#cookiesset-name--value---options--
      return instance.set(name, undefined, options);
    }
    assert(helper.isString(value), 'cookie value must be a string');
    // http://browsercookielimits.squawky.net/
    if (value.length >= 4094) {
      this.app.emit('cookieLimit', {name, value, ctx: this});
    }
    this[COOKIE_STORE][name] = value;
    // set cookie
    return instance.set(name, value, options);
  },
  /**
   * get controller instance
   * @param {String} name
   * @param {String} m
   */
  controller(name, m = this.module) {
    let mcls = think.app.controllers;
    if (this.app.modules.length) {
      mcls = think.app.controllers[m || 'common'] || {};
    }
    const Cls = mcls[name];
    assert(Cls, `can not find controller: ${name}`);
    return new Cls(this);
  },
  /**
   * get service
   * @param {String} name
   * @param {String} m
   */
  service(...args) {
    return think.service(...args);
  },
  /**
   * download
   * @param {String} filepath
   * @param {String} filename
   */
  download(filepath, filename) {
    assert(filepath, 'filepath can not be empty');
    this.type = path.extname(filepath);
    filename = filename || path.basename(filepath);
    const contentDisposition = this.response.get('Content-Disposition');
    if (!contentDisposition) {
      this.attachment(filename);
    }
    const stream = fs.createReadStream(filepath);
    this.body = stream;
    onFinished(this.res, () => {
      destroy(stream);
    });
  }
};
<commit_msg>Optimize download method<commit_after>const helper = require('think-helper');
const assert = require('assert');
const Cookies = require('cookies');
const url = require('url');

const PARAM = Symbol('context-param');
const POST = Symbol('context-post');
const FILE = Symbol('context-file');
const COOKIE_STORE = Symbol('cookie-store');
const path = require('path');
const fs = require('fs');
const onFinished = require('on-finished');
const destroy = require('destroy');

/**
 * extend context
 */
module.exports = {
  /**
   * get userAgent header
   */
  get userAgent() {
    return this.header['user-agent'];
  },
  /**
   * get referer header
   */
  referer(onlyHost) {
    return this.referrer(onlyHost);
  },
  /**
   * get referer header
   */
  referrer(onlyHost) {
    const referrer = this.header['referer'];
    if (!referrer || !onlyHost) return referrer;
    return url.parse(referrer).hostname;
  },
  /**
   * is get request
   */
  get isGet() {
    return this.method === 'GET';
  },
  /**
   * is post request
   */
  get isPost() {
    return this.method === 'POST';
  },
  /**
   * is method
   */
  isMethod(method) {
    return this.method === method.toUpperCase();
  },
  /**
   * is command line invoke
   */
  get isCli() {
    return think.isCli;
  },
  /**
   * is ajax request
   */
  isAjax(method) {
    if (method && !this.isMethod(method)) return false;
    return this.header['x-requested-with'] === 'XMLHttpRequest';
  },
  /**
   * is jsonp request
   */
  isJsonp(callbackField = this.config('jsonpCallbackField')) {
    return !!this.param(callbackField);
  },
  /**
   * send jsonp data
   */
  jsonp(data, callbackField = this.config('jsonpCallbackField')) {
    let field = this.param(callbackField);
    // remove unsafe chars
    field = (field || '').replace(/[^\w.]/g, '');
    if (field) {
      data = `${field}(${JSON.stringify(data)})`;
    }
    this.type = this.config('jsonContentType');
    this.body = data;
    return false;
  },
  /**
   * send json data
   */
  json(data) {
    this.type = this.config('jsonContentType');
    this.body = data;
    return false;
  },
  /**
   * send success data
   */
  success(data = '', message = '') {
    const obj = {
      [this.config('errnoField')]: 0,
      [this.config('errmsgField')]: message,
      data
    };
    this.type = this.config('jsonContentType');
    this.body = obj;
    return false;
  },
  /**
   * send fail data
   */
  fail(errno, errmsg = '', data = '') {
    let obj;
    if (helper.isObject(errno)) {
      obj = errno;
    } else {
      if (/^[A-Z_]+$/.test(errno)) {
        const messages = think.app.validators.messages || {};
        const msg = messages[errno];
        if (think.isArray(msg)) {
          [errno, errmsg] = msg;
        }
      }
      if (!think.isNumber(errno)) {
        [data, errmsg, errno] = [errmsg, errno, this.config('defaultErrno')];
      }
      obj = {
        [this.config('errnoField')]: errno,
        [this.config('errmsgField')]: errmsg
      };
      if (data) {
        obj.data = data;
      }
    }
    this.type = this.config('jsonContentType');
    this.body = obj;
    return false;
  },
  /**
   * set expires header
   */
  expires(time) {
    time = helper.ms(time);
    const date = new Date(Date.now() + time);
    this.set('Cache-Control', `max-age=${time}`);
    this.set('Expires', date.toUTCString());
  },
  /**
   * get or set configs
   * @param {String} name
   * @param {Mixed} value
   * @param {String} m
   */
  config(name, value, m = this.module) {
    return think.config(name, value, m);
  },
  /**
   * get or set query data
   * `query` or `get` is already used in koa
   * @param {String} name
   * @param {Mixed} value
   */
  param(name, value) {
    if (!this[PARAM]) {
      this[PARAM] = Object.assign({}, this.query);
    }
    if (!name) return this[PARAM];
    if (helper.isObject(name)) {
      this[PARAM] = Object.assign(this[PARAM], name);
      return this;
    }
    assert(name && helper.isString(name), 'param.name must be a string');
    if (value === undefined) {
      // this.param('a,b')
      if (name.indexOf(',') > -1) {
        const value = {};
        name.split(',').forEach(item => {
          value[item] = this[PARAM][item];
        });
        return value;
      }
      return this[PARAM][name];
    }
    this[PARAM][name] = value;
    return this;
  },
  /**
   * get or set post data
   * @param {String} name
   * @param {Mixed} value
   */
  post(name, value) {
    if (!this[POST]) {
      this[POST] = Object.assign({}, this.request.body && this.request.body.post);
    }
    if (!name) return this[POST];
    if (helper.isObject(name)) {
      this[POST] = Object.assign(this[POST], name);
      return this;
    }
    if (value === undefined) {
      // this.param('a,b')
      if (name.indexOf(',') > -1) {
        const value = {};
        name.split(',').forEach(item => {
          value[item] = this[POST][item];
        });
        return value;
      }
      return this[POST][name];
    }
    this[POST][name] = value;
    return this;
  },
  /**
   * get or set file data
   * @param {String} name
   * @param {Mixed} value
   */
  file(name, value) {
    if (!this[FILE]) {
      this[FILE] = Object.assign({}, this.request.body && this.request.body.file);
    }
    if (!name) return this[FILE];
    if (helper.isObject(name)) {
      this[FILE] = Object.assign(this[FILE], name);
      return this;
    }
    if (value === undefined) {
      return this[FILE][name];
    }
    this[FILE][name] = value;
    return this;
  },
  /**
   * get or set cookie
   * @param {String} name
   * @param {String} value
   * @param {Object} options
   */
  cookie(name, value, options = {}) {
    assert(name && helper.isString(name), 'cookie.name must be a string');
    options = Object.assign({}, this.config('cookie'), options);
    const instance = new Cookies(this.req, this.res, {
      keys: options.keys,
      secure: this.req.secure
    });

    if (!this[COOKIE_STORE]) this[COOKIE_STORE] = {};

    // get cookie
    if (value === undefined) {
      if (this[COOKIE_STORE][name] !== undefined) return this[COOKIE_STORE][name];
      return instance.get(name, options);
    }
    // remove cookie
    if (value === null) {
      delete this[COOKIE_STORE][name];
      // If the value is omitted, an outbound header with an expired date is used to delete the cookie.
      // https://github.com/pillarjs/cookies#cookiesset-name--value---options--
      return instance.set(name, undefined, options);
    }
    assert(helper.isString(value), 'cookie value must be a string');
    // http://browsercookielimits.squawky.net/
    if (value.length >= 4094) {
      this.app.emit('cookieLimit', {name, value, ctx: this});
    }
    this[COOKIE_STORE][name] = value;
    // set cookie
    return instance.set(name, value, options);
  },
  /**
   * get controller instance
   * @param {String} name
   * @param {String} m
   */
  controller(name, m = this.module) {
    let mcls = think.app.controllers;
    if (this.app.modules.length) {
      mcls = think.app.controllers[m || 'common'] || {};
    }
    const Cls = mcls[name];
    assert(Cls, `can not find controller: ${name}`);
    return new Cls(this);
  },
  /**
   * get service
   * @param {String} name
   * @param {String} m
   */
  service(...args) {
    return think.service(...args);
  },
  /**
   * download
   * @param {String} filepath
   * @param {String} filename
   */
  download(filepath, filename = path.basename(filepath)) {
    assert(filepath, 'filepath can not be empty');
    const contentType = this.response.get('Content-Type');
    if (!contentType) {
      this.type = path.extname(filename);
    }
    const contentDisposition = this.response.get('Content-Disposition');
    if (!contentDisposition) {
      this.attachment(filename);
    }
    const stream = fs.createReadStream(filepath);
    this.body = stream;
    onFinished(this.res, () => {
      destroy(stream);
    });
  }
};
<|endoftext|>


<commit_before>/**
 * An array with a fixed height and width.
 *
 * @param width {number} width of the array
 * @param height {number} height of the array
 * @param defaultValue the initial value for the array elements
 * @class
 */
var Fixed2DArray = function Fixed2DArray(width,height,defaultValue) {
  this._width = width;
  this._height = height;
  this._grid = [];
  for (var i = 0; i < width; i++) {
    this._grid[i] = [];
    for (var j = 0; j < height; j++) {
      this._grid[i][j] = defaultValue;
    }
  }
};

/**
 * Returns the height of the grid
 */
Fixed2DArray.prototype.getHeight = function() {
  return this._height;
};

/**
 * Returns the width of the grid
 */
Fixed2DArray.prototype.getWidth = function() {
  return this._width;
};

/**
 * Throws an Error if the given coordinate is invalid.
 *
 * @param x {number} x coordinate
 * @param y {number} y coordinate
 */
Fixed2DArray.prototype.validateCoords = function(x,y) {
  if(x<0 || y<0 || x>=this._width || y>=this._height) {
    throw new Error('fixed-2d-array: the coordinate ('+x+'/'+y+') ' +
                    'are not valid.' +
                    ' The size of this array is ('+this._width+'/'+this._height+')');
  }
};

/**
 * Returns the value for the coordinate.
 *
 * @param x {number} x coordinate
 * @param y {number} y coordinate
 */
Fixed2DArray.prototype.get = function(x,y) {
  this.validateCoords(x,y);
  return this._grid[x][y];
};

/**
 * Sets the value for the coordinate.
 *
 * @param x {number} x coordinate
 * @param y {number} y coordinate
 * @param val new value
 */
Fixed2DArray.prototype.set = function(x,y,val) {
  this.validateCoords(x,y);
  this._grid[x][y] = val;
};

/**
 * Returns all neighbours of the given coordinate.
 *
 * For example:
 *

 * [ ][ ][ ][ ][ ]
 * [ ][*][*][*][ ]
 * [ ][*][X][*][ ]
 * [ ][*][*][*][ ]
 * [ ][ ][ ][ ][ ]
 *

 *
 * The given coordinate is marked with an `X`.
 * The function will return an array containing
 * the values for the fields maked with an `*`.
 *
 * @param x {number} x coordinate
 * @param y {number} y coordinate
 */
Fixed2DArray.prototype.getNeighbours = function(x,y){
  this.validateCoords(x,y);

  var returnArray = [];

  for(var i=x-1; i<=x+1; i++){
    for(var j=y-1; j<=y+1; j++){
      try {
        if(!(i===x && j===y)) {
          var element = this.get(i,j);
          returnArray.push(element);
        }
      }
      catch(e) {
        //this is a field at the edge of the grid.
        //ignore
      }
    }
  }
  return returnArray;
};

module.exports = Fixed2DArray;
<commit_msg>Fix typo in error-message<commit_after>/**
 * An array with a fixed height and width.
 *
 * @param width {number} width of the array
 * @param height {number} height of the array
 * @param defaultValue the initial value for the array elements
 * @class
 */
var Fixed2DArray = function Fixed2DArray(width,height,defaultValue) {
  this._width = width;
  this._height = height;
  this._grid = [];
  for (var i = 0; i < width; i++) {
    this._grid[i] = [];
    for (var j = 0; j < height; j++) {
      this._grid[i][j] = defaultValue;
    }
  }
};

/**
 * Returns the height of the grid
 */
Fixed2DArray.prototype.getHeight = function() {
  return this._height;
};

/**
 * Returns the width of the grid
 */
Fixed2DArray.prototype.getWidth = function() {
  return this._width;
};

/**
 * Throws an Error if the given coordinate is invalid.
 *
 * @param x {number} x coordinate
 * @param y {number} y coordinate
 */
Fixed2DArray.prototype.validateCoords = function(x,y) {
  if(x<0 || y<0 || x>=this._width || y>=this._height) {
    throw new Error('fixed-2d-array: the coordinate ('+x+'/'+y+') ' +
                    'is not valid.' +
                    ' The size of this array is ('+this._width+'/'+this._height+')');
  }
};

/**
 * Returns the value for the coordinate.
 *
 * @param x {number} x coordinate
 * @param y {number} y coordinate
 */
Fixed2DArray.prototype.get = function(x,y) {
  this.validateCoords(x,y);
  return this._grid[x][y];
};

/**
 * Sets the value for the coordinate.
 *
 * @param x {number} x coordinate
 * @param y {number} y coordinate
 * @param val new value
 */
Fixed2DArray.prototype.set = function(x,y,val) {
  this.validateCoords(x,y);
  this._grid[x][y] = val;
};

/**
 * Returns all neighbours of the given coordinate.
 *
 * For example:
 *

 * [ ][ ][ ][ ][ ]
 * [ ][*][*][*][ ]
 * [ ][*][X][*][ ]
 * [ ][*][*][*][ ]
 * [ ][ ][ ][ ][ ]
 *

 *
 * The given coordinate is marked with an `X`.
 * The function will return an array containing
 * the values for the fields maked with an `*`.
 *
 * @param x {number} x coordinate
 * @param y {number} y coordinate
 */
Fixed2DArray.prototype.getNeighbours = function(x,y){
  this.validateCoords(x,y);

  var returnArray = [];

  for(var i=x-1; i<=x+1; i++){
    for(var j=y-1; j<=y+1; j++){
      try {
        if(!(i===x && j===y)) {
          var element = this.get(i,j);
          returnArray.push(element);
        }
      }
      catch(e) {
        //this is a field at the edge of the grid.
        //ignore
      }
    }
  }
  return returnArray;
};

module.exports = Fixed2DArray;
<|endoftext|>


<commit_before>module.exports = handler

var mime = require('mime')
mime.default_type = 'text/turtle'
var fs = require('fs')
var $rdf = require('rdflib')
var debug = require('../debug').handlers
var utils = require('../utils.js')
var error = require('../http-error')
const waterfall = require('run-waterfall')

function handler (req, res, next) {
  req.setEncoding('utf8')
  req.text = ''
  req.on('data', function (chunk) {
    req.text += chunk
  })

  req.on('end', function () {
    patchHandler(req, res, next)
  })
}

function patchHandler (req, res, next) {
  var ldp = req.app.locals.ldp
  debug('PATCH -- ' + req.originalUrl)
  debug('PATCH -- text length: ' + (req.text ? req.text.length : 'undefined2'))
  res.header('MS-Author-Via', 'SPARQL')

  var root = !ldp.idp ? ldp.root : ldp.root + req.hostname + '/'
  var filename = utils.uriToFilename(req.path, root)
  var targetContentType = mime.lookup(filename)
  var patchContentType = req.get('content-type').split(';')[0].trim() // Ignore parameters
  var targetURI = utils.uriAbs(req) + req.originalUrl

  debug('PATCH -- Content-type ' + patchContentType + ' patching target ' + targetContentType + ' <' + targetURI + '>')

  if (patchContentType === 'application/sparql') {
    sparql(filename, targetURI, req.text, function (err, result) {
      if (err) {
        return next(err)
      }
      res.json(result)
      return next()
    })
  } else if (patchContentType === 'application/sparql-update') {
    return sparqlUpdate(filename, targetURI, req.text, function (err, patchKB) {
      if (err) {
        return next(err)
      }

      // subscription.publishDelta(req, res, patchKB, targetURI)
      debug('PATCH -- applied OK (sync)')
      res.send('Patch applied OK\n')
      return next()
    })
  } else {
    return next(error(400, 'Unknown patch content type: ' + patchContentType))
  }
} // postOrPatch

function sparql (filename, targetURI, text, callback) {
  debug('PATCH -- parsing query ...')
  var patchURI = targetURI // @@@ beware the triples from the patch ending up in the same place
  var patchKB = $rdf.graph()
  var targetKB = $rdf.graph()
  var targetContentType = mime.lookup(filename)
  var query = $rdf.SPARQLToQuery(text, false, patchKB, patchURI) // last param not used ATM

  fs.readFile(filename, {encoding: 'utf8'}, function (err, dataIn) {
    if (err) {
      return callback(error(404, 'Patch: Original file read error:' + err))
    }

    debug('PATCH -- File read OK ' + dataIn.length)
    debug('PATCH -- parsing target file ...')

    try {
      $rdf.parse(dataIn, targetKB, targetURI, targetContentType)
    } catch (e) {
      debug('Patch: Target ' + targetContentType + ' file syntax error:' + e)
      return callback(error(500, 'Patch: Target ' + targetContentType + ' file syntax error:' + e))
    }
    debug('PATCH -- Target parsed OK ')

    var bindingsArray = []

    var onBindings = function (bindings) {
      var b = {}
      var v
      var x
      for (v in bindings) {
        if (bindings.hasOwnProperty(v)) {
          x = bindings[v]
          b[v] = x.uri ? {'type': 'uri', 'value': x.uri} : { 'type': 'literal', 'value': x.value }
          if (x.lang) {
            b[v]['xml:lang'] = x.lang
          }
          if (x.dt) {
            b[v].dt = x.dt.uri  // @@@ Correct? @@ check
          }
        }
      }
      debug('PATCH -- bindings: ' + JSON.stringify(b))
      bindingsArray.push(b)
    }

    var onDone = function () {
      debug('PATCH -- Query done, no. bindings: ' + bindingsArray.length)
      return callback(null, {
        'head': {
          'vars': query.vars.map(function (v) {
            return v.toNT()
          })
        },
        'results': {
          'bindings': bindingsArray
        }
      })
    }

    var fetcher = new $rdf.Fetcher(targetKB, 10000, true)
    targetKB.query(query, onBindings, fetcher, onDone)
  })
}

function sparqlUpdate (filename, targetURI, text, callback) {
  var patchURI = targetURI // @@@ beware the triples from the patch ending up in the same place
  var patchKB = $rdf.graph()
  var targetKB = $rdf.graph()
  var targetContentType = mime.lookup(filename)

  debug('PATCH -- parsing patch ...')
  var patchObject
  try {
    // Must parse relative to document's base address but patch doc should get diff URI
    patchObject = $rdf.sparqlUpdateParser(text, patchKB, patchURI)
  } catch (e) {
    return callback(error(400, 'Patch format syntax error:\n' + e + '\n'))
  }
  debug('PATCH -- reading target file ...')

  waterfall([
    (cb) => {
      fs.stat(filename, (err) => {
        if (!err) return cb()

        fs.writeFile(filename, '', (err) => {
          if (err) {
            return cb(error(err, 'Error creating the patch target'))
          }
          cb()
        })
      })
    },
    (cb) => {
      fs.readFile(filename, {encoding: 'utf8'}, function (err, dataIn) {
        if (err) {
          return cb(error(500, 'Error reading the patch target'))
        }

        debug('PATCH -- target read OK ' + dataIn.length + ' bytes. Parsing...')

        try {
          $rdf.parse(dataIn, targetKB, targetURI, targetContentType)
        } catch (e) {
          debug('Patch: Target ' + targetContentType + ' file syntax error:' + e)
          return cb(error(500, 'Patch: Target ' + targetContentType + ' file syntax error:' + e))
        }

        var target = patchKB.sym(targetURI)
        debug('PATCH -- Target parsed OK, patching... ')

        targetKB.applyPatch(patchObject, target, function (err) {
          if (err) {
            var message = err.message || err // returns string at the moment
            debug('PATCH FAILED. Returning 409. Message: \'' + message + '\'')
            return cb(error(409, 'Error when applying the patch'))
          }
          debug('PATCH -- Patched. Writeback URI base ' + targetURI)
          var data = $rdf.serialize(target, targetKB, targetURI, targetContentType)
          // debug('Writeback data: ' + data)

          fs.writeFile(filename, data, {encoding: 'utf8'}, function (err, data) {
            if (err) {
              return cb(error(500, 'Failed to write file back after patch: ' + err))
            }
            debug('PATCH -- applied OK (sync)')
            return cb(null, patchKB)
          })
        })
      })
    }
  ], callback)
}
<commit_msg>Fix crash on PATCH request with no Content-Type (#423)<commit_after>module.exports = handler

var mime = require('mime')
mime.default_type = 'text/turtle'
var fs = require('fs')
var $rdf = require('rdflib')
var debug = require('../debug').handlers
var utils = require('../utils.js')
var error = require('../http-error')
const waterfall = require('run-waterfall')

function handler (req, res, next) {
  req.setEncoding('utf8')
  req.text = ''
  req.on('data', function (chunk) {
    req.text += chunk
  })

  req.on('end', function () {
    patchHandler(req, res, next)
  })
}

function patchHandler (req, res, next) {
  var ldp = req.app.locals.ldp
  debug('PATCH -- ' + req.originalUrl)
  debug('PATCH -- text length: ' + (req.text ? req.text.length : 'undefined2'))
  res.header('MS-Author-Via', 'SPARQL')

  var root = !ldp.idp ? ldp.root : ldp.root + req.hostname + '/'
  var filename = utils.uriToFilename(req.path, root)
  var targetContentType = mime.lookup(filename)
  var patchContentType = req.get('content-type')
    ? req.get('content-type').split(';')[0].trim() // Ignore parameters
    : ''
  var targetURI = utils.uriAbs(req) + req.originalUrl

  debug('PATCH -- Content-type ' + patchContentType + ' patching target ' + targetContentType + ' <' + targetURI + '>')

  if (patchContentType === 'application/sparql') {
    sparql(filename, targetURI, req.text, function (err, result) {
      if (err) {
        return next(err)
      }
      res.json(result)
      return next()
    })
  } else if (patchContentType === 'application/sparql-update') {
    return sparqlUpdate(filename, targetURI, req.text, function (err, patchKB) {
      if (err) {
        return next(err)
      }

      // subscription.publishDelta(req, res, patchKB, targetURI)
      debug('PATCH -- applied OK (sync)')
      res.send('Patch applied OK\n')
      return next()
    })
  } else {
    return next(error(400, 'Unknown patch content type: ' + patchContentType))
  }
} // postOrPatch

function sparql (filename, targetURI, text, callback) {
  debug('PATCH -- parsing query ...')
  var patchURI = targetURI // @@@ beware the triples from the patch ending up in the same place
  var patchKB = $rdf.graph()
  var targetKB = $rdf.graph()
  var targetContentType = mime.lookup(filename)
  var query = $rdf.SPARQLToQuery(text, false, patchKB, patchURI) // last param not used ATM

  fs.readFile(filename, {encoding: 'utf8'}, function (err, dataIn) {
    if (err) {
      return callback(error(404, 'Patch: Original file read error:' + err))
    }

    debug('PATCH -- File read OK ' + dataIn.length)
    debug('PATCH -- parsing target file ...')

    try {
      $rdf.parse(dataIn, targetKB, targetURI, targetContentType)
    } catch (e) {
      debug('Patch: Target ' + targetContentType + ' file syntax error:' + e)
      return callback(error(500, 'Patch: Target ' + targetContentType + ' file syntax error:' + e))
    }
    debug('PATCH -- Target parsed OK ')

    var bindingsArray = []

    var onBindings = function (bindings) {
      var b = {}
      var v
      var x
      for (v in bindings) {
        if (bindings.hasOwnProperty(v)) {
          x = bindings[v]
          b[v] = x.uri ? {'type': 'uri', 'value': x.uri} : { 'type': 'literal', 'value': x.value }
          if (x.lang) {
            b[v]['xml:lang'] = x.lang
          }
          if (x.dt) {
            b[v].dt = x.dt.uri  // @@@ Correct? @@ check
          }
        }
      }
      debug('PATCH -- bindings: ' + JSON.stringify(b))
      bindingsArray.push(b)
    }

    var onDone = function () {
      debug('PATCH -- Query done, no. bindings: ' + bindingsArray.length)
      return callback(null, {
        'head': {
          'vars': query.vars.map(function (v) {
            return v.toNT()
          })
        },
        'results': {
          'bindings': bindingsArray
        }
      })
    }

    var fetcher = new $rdf.Fetcher(targetKB, 10000, true)
    targetKB.query(query, onBindings, fetcher, onDone)
  })
}

function sparqlUpdate (filename, targetURI, text, callback) {
  var patchURI = targetURI // @@@ beware the triples from the patch ending up in the same place
  var patchKB = $rdf.graph()
  var targetKB = $rdf.graph()
  var targetContentType = mime.lookup(filename)

  debug('PATCH -- parsing patch ...')
  var patchObject
  try {
    // Must parse relative to document's base address but patch doc should get diff URI
    patchObject = $rdf.sparqlUpdateParser(text, patchKB, patchURI)
  } catch (e) {
    return callback(error(400, 'Patch format syntax error:\n' + e + '\n'))
  }
  debug('PATCH -- reading target file ...')

  waterfall([
    (cb) => {
      fs.stat(filename, (err) => {
        if (!err) return cb()

        fs.writeFile(filename, '', (err) => {
          if (err) {
            return cb(error(err, 'Error creating the patch target'))
          }
          cb()
        })
      })
    },
    (cb) => {
      fs.readFile(filename, {encoding: 'utf8'}, function (err, dataIn) {
        if (err) {
          return cb(error(500, 'Error reading the patch target'))
        }

        debug('PATCH -- target read OK ' + dataIn.length + ' bytes. Parsing...')

        try {
          $rdf.parse(dataIn, targetKB, targetURI, targetContentType)
        } catch (e) {
          debug('Patch: Target ' + targetContentType + ' file syntax error:' + e)
          return cb(error(500, 'Patch: Target ' + targetContentType + ' file syntax error:' + e))
        }

        var target = patchKB.sym(targetURI)
        debug('PATCH -- Target parsed OK, patching... ')

        targetKB.applyPatch(patchObject, target, function (err) {
          if (err) {
            var message = err.message || err // returns string at the moment
            debug('PATCH FAILED. Returning 409. Message: \'' + message + '\'')
            return cb(error(409, 'Error when applying the patch'))
          }
          debug('PATCH -- Patched. Writeback URI base ' + targetURI)
          var data = $rdf.serialize(target, targetKB, targetURI, targetContentType)
          // debug('Writeback data: ' + data)

          fs.writeFile(filename, data, {encoding: 'utf8'}, function (err, data) {
            if (err) {
              return cb(error(500, 'Failed to write file back after patch: ' + err))
            }
            debug('PATCH -- applied OK (sync)')
            return cb(null, patchKB)
          })
        })
      })
    }
  ], callback)
}
<|endoftext|>


<commit_before>var debug = require('debug')('gap');

var events = require('events');
var util = require('util');

var Gap = function(hci) {
  this._hci = hci;

  this._scanState = null;
  this._discoveries = {};

  this._hci.on('error', this.onHciError.bind(this));
  this._hci.on('leScanParametersSet', this.onHciLeScanParametersSet.bind(this));
  this._hci.on('leScanEnableSet', this.onHciLeScanEnableSet.bind(this));
  this._hci.on('leAdvertisingReport', this.onHciLeAdvertisingReport.bind(this));
};

util.inherits(Gap, events.EventEmitter);

Gap.prototype.startScanning = function(allowDuplicates) {
  this._scanState = 'starting';

  this._hci.setScanEnabled(true, !allowDuplicates);
};

Gap.prototype.stopScanning = function() {
  this._scanState = 'stopping';

  this._hci.setScanEnabled(false, true);
};

Gap.prototype.onHciError = function(error) {

};

Gap.prototype.onHciLeScanParametersSet = function() {

};

Gap.prototype.onHciLeScanEnableSet = function() {
  if (this._scanState === 'starting') {
    this._scanState = 'stared';

    this.emit('scanStart');
  } else if (this._scanState === 'stopping') {
    this._scanState = 'stopped';

    this.emit('scanStop');
  }
};

Gap.prototype.onHciLeAdvertisingReport = function(status, type, address, addressType, eir, rssi) {
  var previouslyDiscovered = !!this._discoveries[address];
  var advertisement =  previouslyDiscovered ? this._discoveries[address].advertisement : {
    localName: undefined,
    txPowerLevel: undefined,
    manufacturerData: undefined,
    serviceData: [],
    serviceUuids: []
  };

  var discoveryCount = previouslyDiscovered ? this._discoveries[address].count : 0;
  var hasScanResponse = previouslyDiscovered ? this._discoveries[address].hasScanResponse : false;

  if (type === 0x04) {
    hasScanResponse = true;
  } else {
    // reset service data every non-scan response event
    advertisement.serviceData = [];
    advertisement.serviceUuids = [];
  }

  discoveryCount++;

  var i = 0;
  var j = 0;
  var serviceUuid = null;

  while ((i + 1) < eir.length) {
    var length = eir.readUInt8(i);

    if (length < 1) {
      debug('invalid EIR data, length = ' + length);
      break;
    }

    var eirType = eir.readUInt8(i + 1); // https://www.bluetooth.org/en-us/specification/assigned-numbers/generic-access-profile

    if ((i + length + 1) > eir.length) {
      debug('invalid EIR data, out of range of buffer length');
      break;
    }

    var bytes = eir.slice(i + 2).slice(0, length - 1);

    switch(eirType) {
      case 0x02: // Incomplete List of 16-bit Service Class UUID
      case 0x03: // Complete List of 16-bit Service Class UUIDs
        for (j = 0; j < bytes.length; j += 2) {
          serviceUuid = bytes.readUInt16LE(j).toString(16);
          if (advertisement.serviceUuids.indexOf(serviceUuid) === -1) {
            advertisement.serviceUuids.push(serviceUuid);
          }
        }
        break;

      case 0x06: // Incomplete List of 128-bit Service Class UUIDs
      case 0x07: // Complete List of 128-bit Service Class UUIDs
        for (j = 0; j < bytes.length; j += 16) {
          serviceUuid = bytes.slice(j, j + 16).toString('hex').match(/.{1,2}/g).reverse().join('');
          if (advertisement.serviceUuids.indexOf(serviceUuid) === -1) {
            advertisement.serviceUuids.push(serviceUuid);
          }
        }
        break;

      case 0x08: // Shortened Local Name
      case 0x09: // Complete Local Name»
        advertisement.localName = bytes.toString('utf8');
        break;

      case 0x0a: // Tx Power Level
        advertisement.txPowerLevel = bytes.readInt8(0);
        break;

      case 0x16: // Service Data, there can be multiple occurences
        var serviceDataUuid = bytes.slice(0, 2).toString('hex').match(/.{1,2}/g).reverse().join('');
        var serviceData = bytes.slice(2, bytes.length);

        advertisement.serviceData.push({
          uuid: serviceDataUuid,
          data: serviceData
        });
        break;

      case 0xff: // Manufacturer Specific Data
        advertisement.manufacturerData = bytes;
        break;
    }

    i += (length + 1);
  }

  debug('advertisement = ' + JSON.stringify(advertisement, null, 0));

  var connectable = (type === 0x04) ? this._discoveries[address].connectable : (type !== 0x03);

  this._discoveries[address] = {
    address: address,
    addressType: addressType,
    connectable: connectable,
    advertisement: advertisement,
    rssi: rssi,
    count: discoveryCount,
    hasScanResponse: hasScanResponse
  };

  // only report after a scan response event or more than one discovery without a scan response, so more data can be collected
  if (type === 0x04 || (discoveryCount > 1 && !hasScanResponse) || process.env.NOBLE_REPORT_ALL_HCI_EVENTS) {
    this.emit('discover', status, address, addressType, connectable, advertisement, rssi);
  }
};

module.exports = Gap;
<commit_msg>Fix a potential exception when accessing a non existent element<commit_after>var debug = require('debug')('gap');

var events = require('events');
var util = require('util');

var Gap = function(hci) {
  this._hci = hci;

  this._scanState = null;
  this._discoveries = {};

  this._hci.on('error', this.onHciError.bind(this));
  this._hci.on('leScanParametersSet', this.onHciLeScanParametersSet.bind(this));
  this._hci.on('leScanEnableSet', this.onHciLeScanEnableSet.bind(this));
  this._hci.on('leAdvertisingReport', this.onHciLeAdvertisingReport.bind(this));
};

util.inherits(Gap, events.EventEmitter);

Gap.prototype.startScanning = function(allowDuplicates) {
  this._scanState = 'starting';

  this._hci.setScanEnabled(true, !allowDuplicates);
};

Gap.prototype.stopScanning = function() {
  this._scanState = 'stopping';

  this._hci.setScanEnabled(false, true);
};

Gap.prototype.onHciError = function(error) {

};

Gap.prototype.onHciLeScanParametersSet = function() {

};

Gap.prototype.onHciLeScanEnableSet = function() {
  if (this._scanState === 'starting') {
    this._scanState = 'stared';

    this.emit('scanStart');
  } else if (this._scanState === 'stopping') {
    this._scanState = 'stopped';

    this.emit('scanStop');
  }
};

Gap.prototype.onHciLeAdvertisingReport = function(status, type, address, addressType, eir, rssi) {
  var previouslyDiscovered = !!this._discoveries[address];
  var advertisement =  previouslyDiscovered ? this._discoveries[address].advertisement : {
    localName: undefined,
    txPowerLevel: undefined,
    manufacturerData: undefined,
    serviceData: [],
    serviceUuids: []
  };

  var discoveryCount = previouslyDiscovered ? this._discoveries[address].count : 0;
  var hasScanResponse = previouslyDiscovered ? this._discoveries[address].hasScanResponse : false;

  if (type === 0x04) {
    hasScanResponse = true;
  } else {
    // reset service data every non-scan response event
    advertisement.serviceData = [];
    advertisement.serviceUuids = [];
  }

  discoveryCount++;

  var i = 0;
  var j = 0;
  var serviceUuid = null;

  while ((i + 1) < eir.length) {
    var length = eir.readUInt8(i);

    if (length < 1) {
      debug('invalid EIR data, length = ' + length);
      break;
    }

    var eirType = eir.readUInt8(i + 1); // https://www.bluetooth.org/en-us/specification/assigned-numbers/generic-access-profile

    if ((i + length + 1) > eir.length) {
      debug('invalid EIR data, out of range of buffer length');
      break;
    }

    var bytes = eir.slice(i + 2).slice(0, length - 1);

    switch(eirType) {
      case 0x02: // Incomplete List of 16-bit Service Class UUID
      case 0x03: // Complete List of 16-bit Service Class UUIDs
        for (j = 0; j < bytes.length; j += 2) {
          serviceUuid = bytes.readUInt16LE(j).toString(16);
          if (advertisement.serviceUuids.indexOf(serviceUuid) === -1) {
            advertisement.serviceUuids.push(serviceUuid);
          }
        }
        break;

      case 0x06: // Incomplete List of 128-bit Service Class UUIDs
      case 0x07: // Complete List of 128-bit Service Class UUIDs
        for (j = 0; j < bytes.length; j += 16) {
          serviceUuid = bytes.slice(j, j + 16).toString('hex').match(/.{1,2}/g).reverse().join('');
          if (advertisement.serviceUuids.indexOf(serviceUuid) === -1) {
            advertisement.serviceUuids.push(serviceUuid);
          }
        }
        break;

      case 0x08: // Shortened Local Name
      case 0x09: // Complete Local Name»
        advertisement.localName = bytes.toString('utf8');
        break;

      case 0x0a: // Tx Power Level
        advertisement.txPowerLevel = bytes.readInt8(0);
        break;

      case 0x16: // Service Data, there can be multiple occurences
        var serviceDataUuid = bytes.slice(0, 2).toString('hex').match(/.{1,2}/g).reverse().join('');
        var serviceData = bytes.slice(2, bytes.length);

        advertisement.serviceData.push({
          uuid: serviceDataUuid,
          data: serviceData
        });
        break;

      case 0xff: // Manufacturer Specific Data
        advertisement.manufacturerData = bytes;
        break;
    }

    i += (length + 1);
  }

  debug('advertisement = ' + JSON.stringify(advertisement, null, 0));

  var connectable = (type === 0x04 && previouslyDiscovered) ? this._discoveries[address].connectable : (type !== 0x03);

  this._discoveries[address] = {
    address: address,
    addressType: addressType,
    connectable: connectable,
    advertisement: advertisement,
    rssi: rssi,
    count: discoveryCount,
    hasScanResponse: hasScanResponse
  };

  // only report after a scan response event or more than one discovery without a scan response, so more data can be collected
  if (type === 0x04 || (discoveryCount > 1 && !hasScanResponse) || process.env.NOBLE_REPORT_ALL_HCI_EVENTS) {
    this.emit('discover', status, address, addressType, connectable, advertisement, rssi);
  }
};

module.exports = Gap;
<|endoftext|>


<commit_before>/*
 * html-snapshots.js
 *
 * Produce html snapshots using for a website for SEO purposes.
 * This is required for javascript SPAs or ajax page output.
 * By default, uses a selector to search content to determine if
 *   a page is "ready" for its html snapshot.
 *
 * Copyright (c) 2013, 2014, Alex Grant, LocalNerve, contributors
 * Licensed under the MIT license.
 */
var spawn = require("child_process").spawn;
var path = require("path");
var rimraf = require("rimraf").sync;
var asyncLib = require("async");
var _ = require("lodash");

var common = require("./common");
var inputFactory = require("./input-generators");
var async = require("./async");
var phantomDir = "./phantom";
var snapshotScript = path.join(phantomDir, "default.js");

/**
 * Determine the default phantomJS module path. This is overridden by the
 * phatomjs option.
 *
 * This function, (technique and concerns) originated from karma-phantomjs-launcher:
 *
 * The MIT License
 * Copyright (C) 2011-2013 Google, Inc.

 * Permission is hereby granted, free of charge, to any person obtaining a copy of
 * this software and associated documentation files (the "Software"), to deal in
 * the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
 * of the Software, and to permit persons to whom the Software is furnished to do
 * so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 */
var defaultPhantomJSExePath = function () {
  // If the path we're given by phantomjs is to a .cmd, it is pointing to a global copy.
  // Using the cmd as the process to execute causes problems cleaning up the processes
  // so we walk from the cmd to the phantomjs.exe and use that instead.

  var phantomSource = require('phantomjs').path;

  if (path.extname(phantomSource).toLowerCase() === '.cmd') {
    return path.join(path.dirname( phantomSource ), '//node_modules//phantomjs//lib//phantom//phantomjs.exe');
  }

  return phantomSource;
};

/**
 * This module's defaults
 */
var defaults = {
  input: "robots",
  phantomjs: defaultPhantomJSExePath(),
  snapshotScript: path.join(__dirname, snapshotScript),
  outputDirClean: false,
  pollInterval: 500,
  processLimit: 4
};

/**
 * The worker task that launches phantomjs
 */
function worker(input, options, notifier, qcb) {
  var cp,
      customModule,
      snapshotScript = options.snapshotScript,
      phantomjsOptions = Array.isArray(input.phantomjsOptions) ? input.phantomjsOptions : [input.phantomjsOptions];

  // map snapshotScript object script to a real path
  if (_.isObject(options.snapshotScript)) {
    snapshotScript = path.join(__dirname, phantomDir, options.snapshotScript.script) + ".js";
    customModule = options.snapshotScript.module;
  }

  cp = spawn(
    options.phantomjs,
    phantomjsOptions.concat(
    [
      snapshotScript,
      input.outputFile,
      input.url,
      input.selector,
      input.timeout,
      input.checkInterval,
      input.useJQuery,
      customModule
    ]), { cwd: process.cwd(), stdio: "inherit", detached: true }
  );

  cp.on("error", function(e) {
    notifier.remove(input.outputFile);
    notifier.setError(e);
    console.error(e);
    qcb(e);
  });

  cp.on("exit", function(code) {
    qcb(code);
  });

  // start counting
  notifier.add(input.outputFile, input.timeout);
}

module.exports = {
  /**
   * Run all the snapshots using the requested inputGenerator
   */
  run: function(options, listener) {
    options = options || {};

    // ensure our defaults are represented in the options
    common.ensure(options, defaults);

    // create the inputGenerator, default to robots
    var inputGenerator = inputFactory.create(options.input);

    // clean the snapshot output directory
    if (options.outputDirClean) {
      rimraf(options.outputDir);
    }

    // start async completion notification if a listener was supplied
    var result = true, notifier = new async.Notifier();
    if (listener) {
      result = notifier.start(options.pollInterval, listener, inputGenerator);
    }

    if (result) {
      // create a worker queue with a parallel process limit
      var q = asyncLib.queue(function(task, callback) {
        task(_.once(callback));
      }, options.processLimit);

      // have the queue call notifier.empty when last item
      //  from the queue is given to a worker.
      q.empty = notifier.qEmpty;

      // expose abort callback to input generators via options
      options._abort = function(err) {
        notifier.abort(q, err);
      };

      // generate input for the snapshots
      result = inputGenerator.run(options, function(input) {
        // give the worker the input and place into the queue
        q.push(_.partial(worker, input, options, notifier));
      });
    }

    return result;
  }
};
<commit_msg>update google snippet<commit_after>/*
 * html-snapshots.js
 *
 * Produce html snapshots using for a website for SEO purposes.
 * This is required for javascript SPAs or ajax page output.
 * By default, uses a selector to search content to determine if
 *   a page is "ready" for its html snapshot.
 *
 * Copyright (c) 2013, 2014, Alex Grant, LocalNerve, contributors
 * Licensed under the MIT license.
 */
var spawn = require("child_process").spawn;
var path = require("path");
var rimraf = require("rimraf").sync;
var asyncLib = require("async");
var _ = require("lodash");

var common = require("./common");
var inputFactory = require("./input-generators");
var async = require("./async");
var phantomDir = "./phantom";
var snapshotScript = path.join(phantomDir, "default.js");

/**
 * Determine the default phantomJS module path. This is overridden by the
 * phatomjs option.
 *
 * This function, (technique and concerns) originated from karma-phantomjs-launcher:
 *
 * The MIT License
 * Copyright (C) 2011-2013 Google, Inc.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
 * this software and associated documentation files (the "Software"), to deal in
 * the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
 * of the Software, and to permit persons to whom the Software is furnished to do
 * so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 */
var defaultPhantomJSExePath = function () {
  // If the path we're given by phantomjs is to a .cmd, it is pointing to a global copy.
  // Using the cmd as the process to execute causes problems cleaning up the processes
  // so we walk from the cmd to the phantomjs.exe and use that instead.

  var phantomSource = require("phantomjs").path;

  if (path.extname(phantomSource).toLowerCase() === ".cmd") {
    return path.join(path.dirname( phantomSource ), "//node_modules//phantomjs//lib//phantom//phantomjs.exe");
  }

  return phantomSource;
};

/**
 * This module's defaults
 */
var defaults = {
  input: "robots",
  phantomjs: defaultPhantomJSExePath(),
  snapshotScript: path.join(__dirname, snapshotScript),
  outputDirClean: false,
  pollInterval: 500,
  processLimit: 4
};

/**
 * The worker task that launches phantomjs
 */
function worker(input, options, notifier, qcb) {
  var cp,
      customModule,
      snapshotScript = options.snapshotScript,
      phantomjsOptions = Array.isArray(input.phantomjsOptions) ? input.phantomjsOptions : [input.phantomjsOptions];

  // map snapshotScript object script to a real path
  if (_.isObject(options.snapshotScript)) {
    snapshotScript = path.join(__dirname, phantomDir, options.snapshotScript.script) + ".js";
    customModule = options.snapshotScript.module;
  }

  cp = spawn(
    options.phantomjs,
    phantomjsOptions.concat(
    [
      snapshotScript,
      input.outputFile,
      input.url,
      input.selector,
      input.timeout,
      input.checkInterval,
      input.useJQuery,
      customModule
    ]), { cwd: process.cwd(), stdio: "inherit", detached: true }
  );

  cp.on("error", function(e) {
    notifier.remove(input.outputFile);
    notifier.setError(e);
    console.error(e);
    qcb(e);
  });

  cp.on("exit", function(code) {
    qcb(code);
  });

  // start counting
  notifier.add(input.outputFile, input.timeout);
}

module.exports = {
  /**
   * Run all the snapshots using the requested inputGenerator
   */
  run: function(options, listener) {
    options = options || {};

    // ensure our defaults are represented in the options
    common.ensure(options, defaults);

    // create the inputGenerator, default to robots
    var inputGenerator = inputFactory.create(options.input);

    // clean the snapshot output directory
    if (options.outputDirClean) {
      rimraf(options.outputDir);
    }

    // start async completion notification if a listener was supplied
    var result = true, notifier = new async.Notifier();
    if (listener) {
      result = notifier.start(options.pollInterval, listener, inputGenerator);
    }

    if (result) {
      // create a worker queue with a parallel process limit
      var q = asyncLib.queue(function(task, callback) {
        task(_.once(callback));
      }, options.processLimit);

      // have the queue call notifier.empty when last item
      //  from the queue is given to a worker.
      q.empty = notifier.qEmpty;

      // expose abort callback to input generators via options
      options._abort = function(err) {
        notifier.abort(q, err);
      };

      // generate input for the snapshots
      result = inputGenerator.run(options, function(input) {
        // give the worker the input and place into the queue
        q.push(_.partial(worker, input, options, notifier));
      });
    }

    return result;
  }
};
<|endoftext|>


<commit_before>import { Meteor } from 'meteor/meteor'
import { Roles } from '../globals-lib'

const NeuronCatalogApp = {}

NeuronCatalogApp.isSandstorm = function (user, roles) {
  if (Meteor.settings != null) {
    if (Meteor.settings.public != null) {
      return Meteor.settings.public.sandstorm
    }
  }
  return false
}

if (!NeuronCatalogApp.isSandstorm()) {
  NeuronCatalogApp.checkRole = Roles.userIsInRole
} else {
  NeuronCatalogApp.checkRole = function (user, roles) {
    // Implement behavior of Roles.userIsInRole for Sandstorm.
    if (!(user != null)) {
      return false
    }
    let doc
    if (typeof user === 'string') {
      doc = Meteor.users.findOne({_id: user})
    }
    if (typeof user === 'object') {
      doc = user
    }

    if (!doc) {
      return false
    }
    if (!(doc.services != null)) {
      return false
    }
    if (!(doc.services.sandstorm != null)) {
      return false
    }
    if (!(doc.services.sandstorm.permissions != null)) {
      return false
    }

    for (let i = 0; i < doc.services.sandstorm.permissions.length; i++) {
      let has_permission = doc.services.sandstorm.permissions[i]
      if (__in__(has_permission, roles)) {
        return true
      }
    }
    return false
  }
}

function __in__ (needle, haystack) {
  return haystack.indexOf(needle) >= 0
}

export { NeuronCatalogApp }
<commit_msg>simplify sandstorm module<commit_after>import { Meteor } from 'meteor/meteor'
import { Roles } from '../globals-lib'

function isSandstorm () {
  if (Meteor.settings && Meteor.settings.public) {
    return Meteor.settings.public.sandstorm
  }
  return false
}

function sandstormCheckRole (user, roles) {
  // Implement behavior of Roles.userIsInRole for Sandstorm.
  if (user === null) {
    return false
  }
  const doc = (typeof user === 'string') ? Meteor.users.findOne({_id: user}) : user

  if (doc && doc.services && doc.services.sandstorm && doc.services.sandstorm.permissions) {
    for (let i = 0; i < doc.services.sandstorm.permissions.length; i++) {
      let has_permission = doc.services.sandstorm.permissions[i]
      if (roles.indexOf(has_permission) >= 0) {
        return true
      }
    }
  }
  return false
}

let NeuronCatalogApp
if (isSandstorm()) {
  NeuronCatalogApp = {
    isSandstorm () { return true },
    checkRole: sandstormCheckRole
  }
} else {
  NeuronCatalogApp = {
    isSandstorm () { return false },
    checkRole: Roles.userIsInRole
  }
}

export { NeuronCatalogApp }
<|endoftext|>


<commit_before>var qs = require('querystring');
var iconv = require('iconv-lite');
var vm = require('vm');
var util = require('../util');
var extend = require('util')._extend;
var Transform = require('pipestream').Transform;
var WhistleTransform = util.WhistleTransform;
var ReplacePatternTransform = util.ReplacePatternTransform;
var ReplaceStringTransform = util.ReplaceStringTransform;
var FileWriterTransform = util.FileWriterTransform;
var D_CRLF = new Buffer('\r\n');
var MAX_REQ_SIZE = 256 * 1024;
var REQ_TYPE = {
		urlencoded: 'application/x-www-form-urlencoded',
		form: 'application/x-www-form-urlencoded',
		json: 'application/json',
		xml: 'text/xml',
		text: 'text/plain',
		upload: 'multipart/form-data',
		multipart: 'multipart/form-data',
		defaultType: 'application/octet-stream'
};

/**
 * 处理请求数据
 *
 * @param req：method、body、headers，top，bottom，speed、delay，charset,timeout
 * @param data
 */
function handleReq(req, data) {
	req.method = data.method || req.method;
	req.timeout = parseInt(data.timeout, 10);
	extend(req.headers, data.headers);
	var method = req.method = typeof req.method != 'string' ? 'GET' : req.method.toUpperCase();
	if (!util.hasRequestBody(method)) {
		delete data.top;
		delete data.bottom;
		delete data.body;
	} else if (data.top || data.bottom || data.body) {
		delete req.headers['content-length'];
	}

	!util.isEmptyObject(data) && req.add(new WhistleTransform(data));
}

function handleCors(data, cors) {
	if (!cors) {
		return;
	}

	if (cors.origin !== undefined) {
		util.setHeader(data, 'origin', cors.origin);
	}

	if (cors.method !== undefined) {
		util.setHeader(data, 'access-control-request-method', cors.method);
	}

	if (cors.headers !== undefined) {
		util.setHeader(data, 'access-control-request-headers', cors.headers);
	}
}

function handleAuth(data, auth) {
	if (!auth) {
		return;
	}

	var basic = [];
	auth.username != null && basic.push(auth.username);
	auth.password != null && basic.push(auth.password);
	if (basic = basic.join(':')) {
		util.setHeader(data, 'authorization', 'Basic ' + new Buffer(basic).toString('base64'));
	}
}

function handleCookies(data, cookies, curCookies) {
	var list = cookies && Object.keys(cookies);
	if (list && list.length) {
		var result = {};
		if (curCookies && typeof curCookies == 'string') {
			curCookies.split(/;\s*/g)
					.forEach(function(cookie) {
						var index = cookie.indexOf('=');
						if (index == -1) {
							result[cookie] = null;
						} else {
							result[cookie.substring(0, index)] = cookie.substring(index + 1);
						}
					});
		}

		list.forEach(function(name) {
			var value = cookies[name];
			value = value && typeof value == 'object' ? value.value : value;
			result[util.encodeURIComponent(name)] = value ? util.encodeURIComponent(value) : value;
		});

		cookies = Object.keys(result).map(function(name) {
			var value = result[name];
			return name + (value == null ? '' : '=' + value);
		}).join('; ');
		util.setHeader(data, 'cookie', cookies);
	}
}

function handleParams(req, params) {
	var _params = params;
	if (!(params = params && qs.stringify(params))) {
		return;
	}

	if (!req.method || /^GET$/i.test(req.method)) {
		req.options.search = util.replaceUrlQueryString(req.options.search, params);
		req.options.path = util.replaceUrlQueryString(req.options.path, params);
		req.options.href = util.replaceUrlQueryString(req.options.href, params);
	} else if (util.isUrlEncoded(req)) {
		delete req.headers['content-length'];
		var transform = new Transform();
		var buffer, interrupt;
		transform._transform = function(chunk, encoding, callback) {
			if (chunk) {
				if (!interrupt) {
					buffer = buffer ? Buffer.concat([buffer, chunk]) : chunk;
					chunk = null;
					if (buffer.length > MAX_REQ_SIZE) {
						interrupt = true;
						chunk = buffer;
						buffer = null;
					}
				}
			} else if (buffer) {
				var body = buffer + '';
				var isGBK = body.indexOf('�') == -1;
				if (isGBK) {
					body = iconv.decode(buffer, 'GB18030');
					body = util.replaceQueryString(body, params);
					chunk = iconv.encode(body, 'GB18030');
				} else {
					chunk = new Buffer(body);
				}
				buffer = null;
			} else {
				chunk = new Buffer(params);
			}

			callback(null, chunk);
		};
		req.add(transform);
	} else if (util.isUploadForm(req) &&
			/;\s*boundary=([^;]+)/i.test(req.headers['content-type'])) {
		var boundary = new Buffer('--' + RegExp.$1 + '\r\n');
		var endBoundary = new Buffer('--' + RegExp.$1 + '--');
		var length = boundary.length;
		var transform = new Transform();
		delete req.headers['content-length'];
		params = _params;
		transform._transform = function(chunk, encoding, callback) {
			if (chunk) {
//				chunk = this._restBuffer ? this._restBuffer.concat(chunk) : chunk;
//				if (this._startHeadersIndex) {
//
//				} else {
//					var index = util.indexOfBuffer(chunk, boundary);
//					if (index != -1) {
//						this._startHeadersIndex = index + length;
//						this._restBuffer = chunk.slice(this._startHeadersIndex);
//						chunk = chunk.slice(0, this._startHeadersIndex);
//					}
//				}
			} else if (this._restBuffer && this._restBuffer.length) {
//				chunk = this._restBuffer;
			}

			callback(null, chunk);
		};
		req.add(transform);
	}
}

function removeDisableProps(req) {
	var disable = req.disable;
	var headers = req.headers;

	if (disable.ua) {
		delete headers['user-agent'];
	}

	if (disable.cookie || disable.cookies ||
			disable.reqCookie || disable.reqCookies) {
		delete headers.cookie;
	}

	if (disable.referer) {
		delete headers.referer;
	}

	if (disable.cache) {
		delete headers['if-modified-since'];
		delete headers['if-none-match'];
		delete headers['last-modified'];
		delete headers.etag;

		headers['pragma'] = 'no-cache';
		headers['cache-control'] = 'no-cache';
	}
}

function handleReplace(req, replacement) {
	if (!util.hasRequestBody(req) || !replacement) {
		return;
	}

	var type = req.headers['content-type'];
	type = util.isUrlEncoded(req) ? 'FORM' : util.getContentType(type);
	if (!type || type == 'IMG') {
		return;
	}

	Object.keys(replacement).forEach(function(pattern) {
		var value = replacement[pattern];
		if (util.isOriginalRegExp(pattern) && (pattern = util.toOriginalRegExp(pattern))) {
			req.addTextTransform(new ReplacePatternTransform(pattern, value));
		} else if (pattern) {
			req.addTextTransform(new ReplaceStringTransform(pattern, value));
		}
	});
}

module.exports = function(req, res, next) {
	var reqRules = req.rules;
	util.parseRuleJson([reqRules.req, reqRules.reqHeaders, reqRules.reqCookies,
	                    reqRules.auth, reqRules.params, reqRules.reqCors, reqRules.reqReplace],
			function(data, headers, cookies, auth, params, cors, replacement) {
		if (reqRules.head && reqRules.head.req) {
			data = extend(reqRules.head.req, data);
		}
		data = data || {};
		if (headers) {
			data.headers =  extend(data.headers || {}, headers);
		}

		if (data.headers) {
			data.headers = util.lowerCaseify(data.headers);
		}

		var method = util.getMatcherValue(reqRules.method);
		if (method) {
			data.method = method;
		}

		var type = util.getMatcherValue(reqRules.reqType);
		if (type) {
			type = type.indexOf('/') != -1 ? type : (REQ_TYPE[type] || REQ_TYPE.defaultType);
			util.setHeader(data, 'content-type', type);
		}

		var referer = util.getMatcherValue(reqRules.referer);
		if (referer) {
			util.setHeader(data, 'referer', referer);
		}

		var ua = util.getMatcherValue(reqRules.ua);
		if (ua) {
			util.setHeader(data, 'user-agent', ua);
		}

		var reqDelay = util.getMatcherValue(reqRules.reqDelay);
		reqDelay = reqDelay && parseInt(reqDelay, 10);
		if (reqDelay > 0) {
			data.delay = reqDelay;
		}

		var reqSpeed = util.getMatcherValue(reqRules.reqSpeed);
		reqSpeed = reqSpeed && parseFloat(reqSpeed);
		if (reqSpeed > 0) {
			data.speed = reqSpeed;
		}

		handleCookies(data, cookies, req.headers.cookie);
		handleAuth(data, auth);
		handleCors(data, cors);

		util.readInjectFiles(data, function(data) {
			util.getRuleValue([reqRules.reqBody, reqRules.reqPrepend, reqRules.reqAppend],
				function(reqBody, reqPrepend, reqAppend) {
					if (reqBody) {
						data.body = reqBody;
					}

					if (reqPrepend) {
						data.top = reqPrepend;
					}

					if (reqAppend) {
						data.append = reqAppend;
					}

					handleReq(req, data);
					handleParams(req, params);
					util.removeUnsupportsHeaders(req.headers);
					removeDisableProps(req);
					handleReplace(req, replacement);
					util.getFileWriters([util.hasRequestBody(req) ? util.getRuleFile(reqRules.reqWrite) : null, util.getRuleFile(reqRules.reqWriteRaw)], function(writer, rawWriter) {
						if (writer) {
							req.add(new FileWriterTransform(writer, req));
						}

						if (rawWriter) {
							req.add(new FileWriterTransform(rawWriter, req, true, true));
						}

						next();
					});
				});
		});
	});
};<commit_msg>indexOfBuffer<commit_after>var qs = require('querystring');
var iconv = require('iconv-lite');
var vm = require('vm');
var util = require('../util');
var extend = require('util')._extend;
var Transform = require('pipestream').Transform;
var WhistleTransform = util.WhistleTransform;
var ReplacePatternTransform = util.ReplacePatternTransform;
var ReplaceStringTransform = util.ReplaceStringTransform;
var FileWriterTransform = util.FileWriterTransform;
var D_CRLF = new Buffer('\r\n\r\n');
var MAX_REQ_SIZE = 256 * 1024;
var REQ_TYPE = {
		urlencoded: 'application/x-www-form-urlencoded',
		form: 'application/x-www-form-urlencoded',
		json: 'application/json',
		xml: 'text/xml',
		text: 'text/plain',
		upload: 'multipart/form-data',
		multipart: 'multipart/form-data',
		defaultType: 'application/octet-stream'
};

/**
 * 处理请求数据
 *
 * @param req：method、body、headers，top，bottom，speed、delay，charset,timeout
 * @param data
 */
function handleReq(req, data) {
	req.method = data.method || req.method;
	req.timeout = parseInt(data.timeout, 10);
	extend(req.headers, data.headers);
	var method = req.method = typeof req.method != 'string' ? 'GET' : req.method.toUpperCase();
	if (!util.hasRequestBody(method)) {
		delete data.top;
		delete data.bottom;
		delete data.body;
	} else if (data.top || data.bottom || data.body) {
		delete req.headers['content-length'];
	}

	!util.isEmptyObject(data) && req.add(new WhistleTransform(data));
}

function handleCors(data, cors) {
	if (!cors) {
		return;
	}

	if (cors.origin !== undefined) {
		util.setHeader(data, 'origin', cors.origin);
	}

	if (cors.method !== undefined) {
		util.setHeader(data, 'access-control-request-method', cors.method);
	}

	if (cors.headers !== undefined) {
		util.setHeader(data, 'access-control-request-headers', cors.headers);
	}
}

function handleAuth(data, auth) {
	if (!auth) {
		return;
	}

	var basic = [];
	auth.username != null && basic.push(auth.username);
	auth.password != null && basic.push(auth.password);
	if (basic = basic.join(':')) {
		util.setHeader(data, 'authorization', 'Basic ' + new Buffer(basic).toString('base64'));
	}
}

function handleCookies(data, cookies, curCookies) {
	var list = cookies && Object.keys(cookies);
	if (list && list.length) {
		var result = {};
		if (curCookies && typeof curCookies == 'string') {
			curCookies.split(/;\s*/g)
					.forEach(function(cookie) {
						var index = cookie.indexOf('=');
						if (index == -1) {
							result[cookie] = null;
						} else {
							result[cookie.substring(0, index)] = cookie.substring(index + 1);
						}
					});
		}

		list.forEach(function(name) {
			var value = cookies[name];
			value = value && typeof value == 'object' ? value.value : value;
			result[util.encodeURIComponent(name)] = value ? util.encodeURIComponent(value) : value;
		});

		cookies = Object.keys(result).map(function(name) {
			var value = result[name];
			return name + (value == null ? '' : '=' + value);
		}).join('; ');
		util.setHeader(data, 'cookie', cookies);
	}
}

function handleParams(req, params) {
	var _params = params;
	if (!(params = params && qs.stringify(params))) {
		return;
	}

	if (!req.method || /^GET$/i.test(req.method)) {
		req.options.search = util.replaceUrlQueryString(req.options.search, params);
		req.options.path = util.replaceUrlQueryString(req.options.path, params);
		req.options.href = util.replaceUrlQueryString(req.options.href, params);
	} else if (util.isUrlEncoded(req)) {
		delete req.headers['content-length'];
		var transform = new Transform();
		var buffer, interrupt;
		transform._transform = function(chunk, encoding, callback) {
			if (chunk) {
				if (!interrupt) {
					buffer = buffer ? Buffer.concat([buffer, chunk]) : chunk;
					chunk = null;
					if (buffer.length > MAX_REQ_SIZE) {
						interrupt = true;
						chunk = buffer;
						buffer = null;
					}
				}
			} else if (buffer) {
				var body = buffer + '';
				var isGBK = body.indexOf('�') == -1;
				if (isGBK) {
					body = iconv.decode(buffer, 'GB18030');
					body = util.replaceQueryString(body, params);
					chunk = iconv.encode(body, 'GB18030');
				} else {
					chunk = new Buffer(body);
				}
				buffer = null;
			} else {
				chunk = new Buffer(params);
			}

			callback(null, chunk);
		};
		req.add(transform);
	} else if (util.isUploadForm(req) &&
			/;\s*boundary=([^;]+)/i.test(req.headers['content-type'])) {
		var boundary = new Buffer('--' + RegExp.$1 + '\r\n');
		var endBoundary = new Buffer('--' + RegExp.$1 + '--');
		var length = boundary.length;
		var transform = new Transform();
		delete req.headers['content-length'];
		params = _params;
		transform._transform = function(chunk, encoding, callback) {
			if (chunk) {
//				chunk = this._restBuffer ? this._restBuffer.concat(chunk) : chunk;
//				var index = 0;
//				if (this._startHeadersIndex) {
//					index = util.indexOfBuffer(chunk, D_CRLF);
//					if (index != -1) {
//
//					}
//				} else {
//					index = util.indexOfBuffer(chunk, boundary);
//					if (index != -1) {
//						this._startHeadersIndex = index = index + length;
//					} else {
//						index = util.indexOfBuffer(chunk, endBoundary);
//						if (index != -1) {
//
//							index = chunk.length;
//						} else {
//							index = Math.max(chunk.length + 1 - length, 0);
//						}
//					}
//				}

				index = chunk.length;

				this._restBuffer = chunk.slice(index);
				chunk = chunk.slice(0, index);
			} else if (this._restBuffer && this._restBuffer.length) {
				chunk = this._restBuffer;
			}

			callback(null, chunk);
		};
		req.add(transform);
	}
}

function removeDisableProps(req) {
	var disable = req.disable;
	var headers = req.headers;

	if (disable.ua) {
		delete headers['user-agent'];
	}

	if (disable.cookie || disable.cookies ||
			disable.reqCookie || disable.reqCookies) {
		delete headers.cookie;
	}

	if (disable.referer) {
		delete headers.referer;
	}

	if (disable.cache) {
		delete headers['if-modified-since'];
		delete headers['if-none-match'];
		delete headers['last-modified'];
		delete headers.etag;

		headers['pragma'] = 'no-cache';
		headers['cache-control'] = 'no-cache';
	}
}

function handleReplace(req, replacement) {
	if (!util.hasRequestBody(req) || !replacement) {
		return;
	}

	var type = req.headers['content-type'];
	type = util.isUrlEncoded(req) ? 'FORM' : util.getContentType(type);
	if (!type || type == 'IMG') {
		return;
	}

	Object.keys(replacement).forEach(function(pattern) {
		var value = replacement[pattern];
		if (util.isOriginalRegExp(pattern) && (pattern = util.toOriginalRegExp(pattern))) {
			req.addTextTransform(new ReplacePatternTransform(pattern, value));
		} else if (pattern) {
			req.addTextTransform(new ReplaceStringTransform(pattern, value));
		}
	});
}

module.exports = function(req, res, next) {
	var reqRules = req.rules;
	util.parseRuleJson([reqRules.req, reqRules.reqHeaders, reqRules.reqCookies,
	                    reqRules.auth, reqRules.params, reqRules.reqCors, reqRules.reqReplace],
			function(data, headers, cookies, auth, params, cors, replacement) {
		if (reqRules.head && reqRules.head.req) {
			data = extend(reqRules.head.req, data);
		}
		data = data || {};
		if (headers) {
			data.headers =  extend(data.headers || {}, headers);
		}

		if (data.headers) {
			data.headers = util.lowerCaseify(data.headers);
		}

		var method = util.getMatcherValue(reqRules.method);
		if (method) {
			data.method = method;
		}

		var type = util.getMatcherValue(reqRules.reqType);
		if (type) {
			type = type.indexOf('/') != -1 ? type : (REQ_TYPE[type] || REQ_TYPE.defaultType);
			util.setHeader(data, 'content-type', type);
		}

		var referer = util.getMatcherValue(reqRules.referer);
		if (referer) {
			util.setHeader(data, 'referer', referer);
		}

		var ua = util.getMatcherValue(reqRules.ua);
		if (ua) {
			util.setHeader(data, 'user-agent', ua);
		}

		var reqDelay = util.getMatcherValue(reqRules.reqDelay);
		reqDelay = reqDelay && parseInt(reqDelay, 10);
		if (reqDelay > 0) {
			data.delay = reqDelay;
		}

		var reqSpeed = util.getMatcherValue(reqRules.reqSpeed);
		reqSpeed = reqSpeed && parseFloat(reqSpeed);
		if (reqSpeed > 0) {
			data.speed = reqSpeed;
		}

		handleCookies(data, cookies, req.headers.cookie);
		handleAuth(data, auth);
		handleCors(data, cors);

		util.readInjectFiles(data, function(data) {
			util.getRuleValue([reqRules.reqBody, reqRules.reqPrepend, reqRules.reqAppend],
				function(reqBody, reqPrepend, reqAppend) {
					if (reqBody) {
						data.body = reqBody;
					}

					if (reqPrepend) {
						data.top = reqPrepend;
					}

					if (reqAppend) {
						data.append = reqAppend;
					}

					handleReq(req, data);
					handleParams(req, params);
					util.removeUnsupportsHeaders(req.headers);
					removeDisableProps(req);
					handleReplace(req, replacement);
					util.getFileWriters([util.hasRequestBody(req) ? util.getRuleFile(reqRules.reqWrite) : null, util.getRuleFile(reqRules.reqWriteRaw)], function(writer, rawWriter) {
						if (writer) {
							req.add(new FileWriterTransform(writer, req));
						}

						if (rawWriter) {
							req.add(new FileWriterTransform(rawWriter, req, true, true));
						}

						next();
					});
				});
		});
	});
};<|endoftext|>


<commit_before>/*jshint module: false */
"use strict";

import Q from "q";
import wire from "wire";

function mapArgs(func) {
    return func.inject.map(function (id) {
        return {
            $ref: id
        };
    });
}

// Looks at a list of dependencies and returns a new list by not including `Opts`.
function createDependenciesWithoutOptsFrom(inject) {
    return inject.filter(function (dependency) {
        return !/Opt$/.test(dependency);
    });
}

// Creates a list of resolved dependencies by checking if the dependency is first an option.
// If it's not, pull from the already resolved dependencies provided by the factory.
function createArgsWithOptsForInstance(Component, factoryArgs, options) {
    var resolvedDependencies = factoryArgs.slice();

    return Component.inject.map(function (dependency) {
        if (/Opt$/.test(dependency)) { // We have an option, look up in options hash.
            var argName = dependency.replace(/Opt$/, "");
            if (argName in options) {
                return options[argName];
            } else {
                throw new Error("Can not resolve dependency: " + dependency);
            }
        } else { // This dependency was already resolved by the component's factory
            return resolvedDependencies.shift();
        }
    });
}

function createFactoryFor(Component, specOpts) {
    function componentFactory() {
        var factoryArgs = [].slice.apply(arguments);

        return function (options) {
            var component;
            var instanceArgs = createArgsWithOptsForInstance(Component, factoryArgs, options);
            instanceArgs.push(options); // Push the entire opts as last argument for config components.

            if (specOpts.isConstructor) {
                component = Object.create(Component.prototype);
                Component.apply(component, instanceArgs);
            } else {
                component = Component.apply(null, instanceArgs);
            }
            return component;
        };
    }

    var inject = Component.inject;
    if (Array.isArray(inject)) {
        inject = createDependenciesWithoutOptsFrom(inject);
    }

    componentFactory.inject = inject;
    return componentFactory;
}

export function config(component, options) {
    options = options || {};
    var args;

    if ("args" in options) {
        args = options.args;
    } else if (typeof component === "function" && Array.isArray(component.inject)) {
        args = mapArgs(component);
    } else {
        args = [];
    }

    var type = "type" in options ? options.type : "create";
    var spec = {};

    if (type === "literal" || type === "module") {
        spec[type] = component;
    } else {
        spec[type] = {
            module: component,
            args: args,
            isConstructor: "isConstructor" in options? options.isConstructor : true
        };
    }

    return spec;
}

export function define() {
    var that = {};
    var specs = {};
    var providerPlugins = {};

    function addProviderFor(key, provider) {
        if (!providerPlugins[key]) {
            providerPlugins[key] = [];
        }

        providerPlugins[key].push(provider);
    }

    function provideHook(key) {
        if (Array.isArray(providerPlugins[key])) {
            providerPlugins[key].forEach((plugin) => plugin(specs[key]));
        }
    }

    function specFactory(options) {
        return function (key, component, callback) {
            if ("isConstructor" in options && /UIFactory$/.test(key)) {
                component = createFactoryFor(component, options);
            }
            specs[key] = config(component, options);

            if (typeof callback === "function") {
                callback(specs[key]);
            }

            provideHook(key);
            return that;
        };
    }

    that.creator = specFactory({ isConstructor: true });

    that.factory = specFactory({ isConstructor: false });

    that.literal = specFactory({ type: "literal" });

    that.module =  specFactory({ type: "module" });

    that.alias = (aliasKey, aliasFor) => {
        return that.factory(aliasKey, (instanceToAlias) => instanceToAlias, (spec) => {
            spec.create.args[0] = { $ref: aliasFor };
        });
    };

    // Sugar for specific chalk app components.
    /* jshint ignore:start */
    that.componentType = (type, ...args) => {
        var specKey = type.replace(/-([a-z])/g, (g) => g[1].toUpperCase());
        specKey += "UIFactory";

        return that.creator(specKey, ...args);
    };
    /* jshint ignore:end */

    that.componentInstance = that.creator;

    that.plugins = (pluginGroup, pluginHash) => {
        var specKey = pluginGroup + "Plugins";
        return that.literal(specKey, pluginHash);
    };

    that.custom = function (key, options) {
        specs[key] = options;
        provideHook(key);

        return that;
    };

    that.provideFor = (key, provider) => {
        addProviderFor(key, provider);

        if (specs[key]) {
            provideHook(key);
        }
    };

    that.spec = function (partialSpec) {
        partialSpec(that);
        return that;
    };

    that.wire = function () {
        return Q.when(wire(specs));
    };

    that.wireAsChildOf = function (parentContainer) {
        return Q.when(parentContainer.wire(specs));
    };

    return that;
}
<commit_msg>New `iocSpecBuilder` registry for `actions`.<commit_after>/*jshint module: false */
"use strict";

import _ from "underscore";
import Q from "q";
import wire from "wire";

function mapArgs(func) {
    return func.inject.map(function (id) {
        return {
            $ref: id
        };
    });
}

// Looks at a list of dependencies and returns a new list by not including `Opts`.
function createDependenciesWithoutOptsFrom(inject) {
    return inject.filter(function (dependency) {
        return !/Opt$/.test(dependency);
    });
}

// Creates a list of resolved dependencies by checking if the dependency is first an option.
// If it's not, pull from the already resolved dependencies provided by the factory.
function createArgsWithOptsForInstance(Component, factoryArgs, options) {
    var resolvedDependencies = factoryArgs.slice();

    return Component.inject.map(function (dependency) {
        if (/Opt$/.test(dependency)) { // We have an option, look up in options hash.
            var argName = dependency.replace(/Opt$/, "");
            if (argName in options) {
                return options[argName];
            } else {
                throw new Error("Can not resolve dependency: " + dependency);
            }
        } else { // This dependency was already resolved by the component's factory
            return resolvedDependencies.shift();
        }
    });
}

function createFactoryFor(Component, specOpts) {
    function componentFactory() {
        var factoryArgs = [].slice.apply(arguments);

        return function (options) {
            var component;
            var instanceArgs = createArgsWithOptsForInstance(Component, factoryArgs, options);
            instanceArgs.push(options); // Push the entire opts as last argument for config components.

            if (specOpts.isConstructor) {
                component = Object.create(Component.prototype);
                Component.apply(component, instanceArgs);
            } else {
                component = Component.apply(null, instanceArgs);
            }
            return component;
        };
    }

    var inject = Component.inject;
    if (Array.isArray(inject)) {
        inject = createDependenciesWithoutOptsFrom(inject);
    }

    componentFactory.inject = inject;
    return componentFactory;
}

export function config(component, options) {
    options = options || {};
    var args;

    if ("args" in options) {
        args = options.args;
    } else if (typeof component === "function" && Array.isArray(component.inject)) {
        args = mapArgs(component);
    } else {
        args = [];
    }

    var type = "type" in options ? options.type : "create";
    var spec = {};

    if (type === "literal" || type === "module") {
        spec[type] = component;
    } else {
        spec[type] = {
            module: component,
            args: args,
            isConstructor: "isConstructor" in options? options.isConstructor : true
        };
    }

    return spec;
}

export function define() {
    var that = {};
    var specs = {};
    var providerPlugins = {};
    var actions = {
        names: [],
        instances: {}
    };

    function saveActionsFrom(container) {
        actions.instances = _.object(
            actions.names.map((name) => [name, container[name]])
        );
    }

    function addProviderFor(key, provider) {
        if (!providerPlugins[key]) {
            providerPlugins[key] = [];
        }

        providerPlugins[key].push(provider);
    }

    function provideHook(key) {
        if (Array.isArray(providerPlugins[key])) {
            providerPlugins[key].forEach((plugin) => plugin(specs[key]));
        }
    }

    function specFactory(options) {
        return function (key, component, callback) {
            if ("isConstructor" in options && /UIFactory$/.test(key)) {
                component = createFactoryFor(component, options);
            }
            specs[key] = config(component, options);

            if (typeof callback === "function") {
                callback(specs[key]);
            }

            provideHook(key);
            return that;
        };
    }

    that.creator = specFactory({ isConstructor: true });

    that.factory = specFactory({ isConstructor: false });

    that.literal = specFactory({ type: "literal" });

    that.module =  specFactory({ type: "module" });

    that.alias = (aliasKey, aliasFor) => {
        return that.factory(aliasKey, (instanceToAlias) => instanceToAlias, (spec) => {
            spec.create.args[0] = { $ref: aliasFor };
        });
    };

    // Sugar for specific chalk app components.
    /* jshint ignore:start */
    that.componentType = (type, ...args) => {
        var specKey = type.replace(/-([a-z])/g, (g) => g[1].toUpperCase());
        specKey += "UIFactory";

        return that.creator(specKey, ...args);
    };
    /* jshint ignore:end */

    that.componentInstance = that.creator;

    that.plugins = (pluginGroup, pluginHash) => {
        var specKey = pluginGroup + "Plugins";
        return that.literal(specKey, pluginHash);
    };

    that.action = (...args) => {
        var name;
        var Action;

        if (args.length === 2) {
            [name, Action] = args;
        } else {
            [Action] = args;
            name = Action.prototype.name;
        }

        actions.names.push(name);
        return that.creator(name, Action);
    };

    that.custom = function (key, options) {
        specs[key] = options;
        provideHook(key);

        return that;
    };

    that.provideFor = (key, provider) => {
        addProviderFor(key, provider);

        if (specs[key]) {
            provideHook(key);
        }
    };

    that.spec = function (partialSpec) {
        partialSpec(that);
        return that;
    };

    that.wire = function () {
        return Q.when(wire(specs))
            .then((container) => {
                saveActionsFrom(container);
                return container;
            });
    };

    that.wireAsChildOf = function (parentContainer) {
        return Q.when(parentContainer.wire(specs));
    };

    Object.defineProperty(that, "actions", {
        get: () => actions.instances,
        enumerable: true
    });

    return that;
}
<|endoftext|>