<commit_before>/***************************************************************************
*
* Copyright (C) 2014 Baidu.com, Inc. All Rights Reserved
*
***************************************************************************/

/**
 * @file server.js ~ 2014-04-26 14:35
 * @author sekiyika (px.pengxing@gmail.com)
 * @description
 *  启动webserver
 */

var ws = require('edp-webserver');
var urlLib = require('url');
var path2RegExp = require('path-to-regexp');
var path = require('path');
var fs = require('fs');
var edp = require('edp-core');

var Render = require('./render');
var util = require('./util');
var AssembleTemplate = require('./AssembleTemplate');
var indexer = require('./indexer');
var cache = require('./cache');

/**
 * 判断请求是否需要代理，如果需要代理，返回代理的文件内容
 * @param {http.ClientRequest} request
 */
function shouldProxy(request, gConf) {
    var referer = request.headers.referer;

    var tpls;

    if(referer) {
        var refs = urlLib.parse(referer, true);
        var query = refs.query.wd;
        var platform = refs.query.dsp || 'pc';

        var renderConf = indexer.getRenderConf(query, platform, gConf);
        if(!renderConf) {
            return;
        }
        tpls = util.extend({}, renderConf.left, renderConf.right);

    } else {
        // 如果没有referer，则运行所有的规则
        tpls = indexer.name2ConfMap;
    }

    var url = request.pathname;

    var config;
    var as;
    var item;
    var location;
    var i;
    for(var name in tpls) {
        config = indexer.name2ConfMap[name];

        as = config.ajaxs;
        if(as && as.length > 0) {
            for(i = 0, l = as.length; i < l; i++) {
                item = as[i];
                location = item.url;

                if ( false
                       || (location instanceof RegExp && location.test(url))
                       || (typeof location === 'string' && path2RegExp( location, null, { sensitive: true } ).exec(url))
                       || (typeof location === 'function' && location( request ) )
                   ) {
                       // 匹配成功
                       if(!item.handler && item.file) {
                           request._proxyHandler = (function(file, dir) {
                               return function() {
                                   if(!fs.existsSync(file)) {
                                       file = path.resolve(dir + path.sep + file);
                                   }
                                   edp.log.info(''
                                        + edp.chalk.yellow('PROXY')
                                        + ' '
                                        + edp.chalk.green(url)
                                        + ' to '
                                        + edp.chalk.green(file)
                                   );
                                   return fs.readFileSync(file);
                               };
                           })(item.file, config.fullpath);
                       } else {
                           edp.log.info(edp.chalk.yellow('PROXY ') + edp.chalk.green(url));

                           request._proxyHandler = item.handler;
                       }
                       return true;
                   }
            }
        }
    }

}

/**
 * 处理proxy的返回
 * @param {Object} context
 */
function handleProxy(context, g) {
    var request = context.request;

    var handler = request._proxyHandler;
    if(typeof handler === 'string') {
        context.content = handler;
    } else if(typeof handler === 'function') {
        context.content = handler(context);
    }

}

/**
 * 渲染
 * @param {Object} context
 * @param {string} root
 * @param {Object} gConf
 */
function render(context, root, gConf) {
    context.stop();

    var queryObj = context.request.query;

    // 从indexer获取本次应该渲染的卡片配置
    var renderConf = indexer.getRenderConf(queryObj.wd, queryObj.dsp, gConf);

    // 在这里进行渲染，拿到渲染后的结果
    var promise = Render.render(
        gConf.php ? gConf.php : 'php',
        renderConf
    );
    promise.done(function(output) {
        var data;
        if(!output) {
            data = {};
        } else {

            try{
            // 防止有notice和warming导致json串不能解析
            output = output.output.replace(/[^{}]*(.*)[^{}]*/g,
                function () {
                    return arguments[1];
                }
            );
            data = JSON.parse(output);
            } catch(e) {
                console.log(e);
            }
        }

        context._renderResult = data;

        context.start();
    });
    promise.fail(function(output) {
        context._renderResult = '
' + output.output + '
';
        context.start();
    });

}

exports.start = function(root, gConf) {

    /**
     * edp webserver的配置文件
     *
     * @type {Object}
     */
    var edpWSConfig = {

        /**
         * 配置ws的documentRoot
         */
        documentRoot: root,
        port: gConf.server.port,
        overrideProxyRequestHeader: function(headers) {
            headers.Host = gConf.proxy.hostname;
        },

        getLocations: function() {
            var locations = [
                {
                    location: function(request) {
                        return shouldProxy(request, gConf);
                    },
                    handler: function(context) {
                        handleProxy(context);
                    }
                },
                {
                    location: /^\/$/,
                    handler: proxy(gConf.proxy.hostname, gConf.proxy.port)
                },
                {
                    location: /^\/s\?.*/,
                    handler: [
                        function(context) {
                            var count = 0;
                            var start = context.start;
                            var stop = context.stop;
                            context.start = function() {
                                count--;
                                if(count === 0) {
                                    context.start = start;
                                    context.stop = stop;
                                    start();
                                }
                            };
                            context.stop = function() {
                                count++;
                                stop();
                            };

                            // proxy将代理返回的内容存放在context.content中
                            cache.proxyContent(context, gConf);

                            // 渲染
                            render(context, root, gConf);
                        },
                        function(context) {
                            //缓存我们在上面的handler中用proxy来产生的内容
                            cache.updateCachedContent(context);
                            // 这里进行解码操作，删除header中的content-encoding
                            delete context.header['content-encoding'];

                            if(typeof context._renderResult === 'string') {
                                context.content = context._renderResult;
                                context.status = 500;
                                return;
                            }

                            context.stop();
                            var zlib = require('zlib');

                            // 当前都是gzip，所以目前只考虑gzip的格式
                            zlib.gunzip(context.content, function(err, buffer) {
                                if(!err) {
                                    var content = buffer.toString();

                                    var queryObj = context.request.query;

                                    // 通过webserver来代理这些静态文件
                                    // var requestHeaders = context.request.headers;
                                    // var proxyedUrl = ['http://',requestHeaders.host,'/proxy_url'].join('');
                                    // content = proxyLinks.proxyLinks(content, proxyedUrl);
                                    content = AssembleTemplate.disassemble(queryObj.dsp, content);

                                    // 在这里进行拼装，将模板和渲染的数据进行拼装
                                    content = AssembleTemplate.assmeble(content, context._renderResult);

                                    context.content = content;
                                    context.start();
                                } else {
                                    // 发生服务器错误
                                    context.status = 500;
                                    // 这一步发生错误，也得到最后啊
                                    context.start();
                                }

                            });
                        }
                    ]
                },
                {
                    location: /^\/proxy_url.*/,
                    handler: [
                        function(context) {
                            var query = context.request.query;
                            if(query.url) {

                                var urlLib = require('url');
                                var url = urlLib.parse(query.url);
                                // 重写request的url
                                context.request.url = url.path;
                                context.request.headers.host = url.hostname;
                                //增加referer，以通过百度对图片的验证
                                context.request.headers.referer = 'http://' + gConf.proxy.hostname;
                                // 代理一下
                                (proxy(url.hostname, url.port ? url.port : 80))(context);
                            }
                        }
                    ]
                }
            ];

            if(gConf.server.getLocations && typeof gConf.server.getLocations === 'function') {
                var locs = gConf.server.getLocations();
                if(locs && locs instanceof Array) {
                    locations = locations.concat(locs);
                }
            }
            locations.push({
                location: /^.*$/,
                handler: proxy(gConf.proxy.hostname, gConf.proxy.port)
            });
            return locations;
        },

        injectResource: function ( res ) {
            for ( var key in res ) {
                global[ key ] = res[ key ];
            }
        }
    };

    ws.start(edpWSConfig);
};
<commit_msg>修复referer里没有query，导致ajaxs模块失效<commit_after>/***************************************************************************
*
* Copyright (C) 2014 Baidu.com, Inc. All Rights Reserved
*
***************************************************************************/

/**
 * @file server.js ~ 2014-04-26 14:35
 * @author sekiyika (px.pengxing@gmail.com)
 * @description
 *  启动webserver
 */

var ws = require('edp-webserver');
var urlLib = require('url');
var path2RegExp = require('path-to-regexp');
var path = require('path');
var fs = require('fs');
var edp = require('edp-core');

var Render = require('./render');
var util = require('./util');
var AssembleTemplate = require('./AssembleTemplate');
var indexer = require('./indexer');
var cache = require('./cache');

/**
 * 判断请求是否需要代理，如果需要代理，返回代理的文件内容
 * @param {http.ClientRequest} request
 */
function shouldProxy(request, gConf) {
    var referer = request.headers.referer;

    var tpls;

    if(referer) {
        var refs = urlLib.parse(referer, true);
        var query = refs.query.wd;
        var platform = refs.query.dsp || 'pc';

        if(!query) {
            // 如果referer没有query，采用全局的
            tpls = indexer.name2ConfMap;
        } else {
            var renderConf = indexer.getRenderConf(query, platform, gConf);
            if(!renderConf) {
                return;
            }
            tpls = util.extend({}, renderConf.left, renderConf.right);
        }

    } else {
        // 如果没有referer，则运行所有的规则
        tpls = indexer.name2ConfMap;
    }

    var url = request.pathname;

    var config;
    var as;
    var item;
    var location;
    var i;
    for(var name in tpls) {
        config = indexer.name2ConfMap[name];

        as = config.ajaxs;
        if(as && as.length > 0) {
            for(i = 0, l = as.length; i < l; i++) {
                item = as[i];
                location = item.url;

                if ( false
                       || (location instanceof RegExp && location.test(url))
                       || (typeof location === 'string' && path2RegExp( location, null, { sensitive: true } ).exec(url))
                       || (typeof location === 'function' && location( request ) )
                   ) {
                       // 匹配成功
                       if(!item.handler && item.file) {
                           request._proxyHandler = (function(file, dir) {
                               return function() {
                                   if(!fs.existsSync(file)) {
                                       file = path.resolve(dir + path.sep + file);
                                   }
                                   edp.log.info(''
                                        + edp.chalk.yellow('PROXY')
                                        + ' '
                                        + edp.chalk.green(url)
                                        + ' to '
                                        + edp.chalk.green(file)
                                   );
                                   return fs.readFileSync(file);
                               };
                           })(item.file, config.fullpath);
                       } else {
                           edp.log.info(edp.chalk.yellow('PROXY ') + edp.chalk.green(url));

                           request._proxyHandler = item.handler;
                       }
                       return true;
                   }
            }
        }
    }

}

/**
 * 处理proxy的返回
 * @param {Object} context
 */
function handleProxy(context, g) {
    var request = context.request;

    var handler = request._proxyHandler;
    if(typeof handler === 'string') {
        context.content = handler;
    } else if(typeof handler === 'function') {
        context.content = handler(context);
    }

}

/**
 * 渲染
 * @param {Object} context
 * @param {string} root
 * @param {Object} gConf
 */
function render(context, root, gConf) {
    context.stop();

    var queryObj = context.request.query;

    // 从indexer获取本次应该渲染的卡片配置
    var renderConf = indexer.getRenderConf(queryObj.wd, queryObj.dsp, gConf);

    // 在这里进行渲染，拿到渲染后的结果
    var promise = Render.render(
        gConf.php ? gConf.php : 'php',
        renderConf
    );
    promise.done(function(output) {
        var data;
        if(!output) {
            data = {};
        } else {

            try{
            // 防止有notice和warming导致json串不能解析
            output = output.output.replace(/[^{}]*(.*)[^{}]*/g,
                function () {
                    return arguments[1];
                }
            );
            data = JSON.parse(output);
            } catch(e) {
                console.log(e);
            }
        }

        context._renderResult = data;

        context.start();
    });
    promise.fail(function(output) {
        context._renderResult = '
' + output.output + '
';
        context.start();
    });

}

exports.start = function(root, gConf) {

    /**
     * edp webserver的配置文件
     *
     * @type {Object}
     */
    var edpWSConfig = {

        /**
         * 配置ws的documentRoot
         */
        documentRoot: root,
        port: gConf.server.port,
        overrideProxyRequestHeader: function(headers) {
            headers.Host = gConf.proxy.hostname;
        },

        getLocations: function() {
            var locations = [
                {
                    location: function(request) {
                        return shouldProxy(request, gConf);
                    },
                    handler: function(context) {
                        handleProxy(context);
                    }
                },
                {
                    location: /^\/$/,
                    handler: proxy(gConf.proxy.hostname, gConf.proxy.port)
                },
                {
                    location: /^\/s\?.*/,
                    handler: [
                        function(context) {
                            var count = 0;
                            var start = context.start;
                            var stop = context.stop;
                            context.start = function() {
                                count--;
                                if(count === 0) {
                                    context.start = start;
                                    context.stop = stop;
                                    start();
                                }
                            };
                            context.stop = function() {
                                count++;
                                stop();
                            };

                            // proxy将代理返回的内容存放在context.content中
                            cache.proxyContent(context, gConf);

                            // 渲染
                            render(context, root, gConf);
                        },
                        function(context) {
                            //缓存我们在上面的handler中用proxy来产生的内容
                            cache.updateCachedContent(context);
                            // 这里进行解码操作，删除header中的content-encoding
                            delete context.header['content-encoding'];

                            if(typeof context._renderResult === 'string') {
                                context.content = context._renderResult;
                                context.status = 500;
                                return;
                            }

                            context.stop();
                            var zlib = require('zlib');

                            // 当前都是gzip，所以目前只考虑gzip的格式
                            zlib.gunzip(context.content, function(err, buffer) {
                                if(!err) {
                                    var content = buffer.toString();

                                    var queryObj = context.request.query;

                                    // 通过webserver来代理这些静态文件
                                    // var requestHeaders = context.request.headers;
                                    // var proxyedUrl = ['http://',requestHeaders.host,'/proxy_url'].join('');
                                    // content = proxyLinks.proxyLinks(content, proxyedUrl);
                                    content = AssembleTemplate.disassemble(queryObj.dsp, content);

                                    // 在这里进行拼装，将模板和渲染的数据进行拼装
                                    content = AssembleTemplate.assmeble(content, context._renderResult);

                                    context.content = content;
                                    context.start();
                                } else {
                                    // 发生服务器错误
                                    context.status = 500;
                                    // 这一步发生错误，也得到最后啊
                                    context.start();
                                }

                            });
                        }
                    ]
                },
                {
                    location: /^\/proxy_url.*/,
                    handler: [
                        function(context) {
                            var query = context.request.query;
                            if(query.url) {

                                var urlLib = require('url');
                                var url = urlLib.parse(query.url);
                                // 重写request的url
                                context.request.url = url.path;
                                context.request.headers.host = url.hostname;
                                //增加referer，以通过百度对图片的验证
                                context.request.headers.referer = 'http://' + gConf.proxy.hostname;
                                // 代理一下
                                (proxy(url.hostname, url.port ? url.port : 80))(context);
                            }
                        }
                    ]
                }
            ];

            if(gConf.server.getLocations && typeof gConf.server.getLocations === 'function') {
                var locs = gConf.server.getLocations();
                if(locs && locs instanceof Array) {
                    locations = locations.concat(locs);
                }
            }
            locations.push({
                location: /^.*$/,
                handler: proxy(gConf.proxy.hostname, gConf.proxy.port)
            });
            return locations;
        },

        injectResource: function ( res ) {
            for ( var key in res ) {
                global[ key ] = res[ key ];
            }
        }
    };

    ws.start(edpWSConfig);
};
<|endoftext|>


<commit_before>const config = require('config')
const Hapi = require('hapi')
const { resolve } = require('path')
const { serial, parallel } = require('items-promise')
const { normalizePath } = require('./utils')

const RouteTablePlugin = require('@bakjs/route-table')
const ShortcutsPlugin = require('@bakjs/shortcuts')
const InputPlugin = require('@bakjs/input')
const CompatPlugin = require('@bakjs/compat')

module.exports = class Server {
  /**
     *
     * @param bak
     */
  constructor (bak) {
    this.bak = bak
    this.options = this.bak.options
  }

  /**
     *
     * @returns {Promise.<Server>}
     */
  async init () {
    // Create server
    this.hapi = new Hapi.Server(this.options.server)

    // Register core plugins
    const corePlugins = [
      CompatPlugin,
      ShortcutsPlugin,
      InputPlugin
    ]

    if (this.options.routeTable) {
      corePlugins.push(RouteTablePlugin)
    }

    console.log(CompatPlugin)

    await serial(corePlugins, this.register.bind(this))

    // Register plugins
    await serial(this.options.registrations, this.register.bind(this))

    // Register routes
    await this.registerRoutes(this.options.routes)

    return this
  }

  /**
     * Start server
     * @returns {Promise}
     * @private
     */
  async start () {
    await this.hapi.initialize()
    await this.hapi.start()
    return this
  }

  /**
     *
     * @param routes
     * @returns {Promise}
     * @private
     */
  async _loadRoutes (routes, dirty = false) {
    if (typeof routes === 'string') {
      dirty = true
      routes = require(resolve(this.options.relativeTo, routes))
    }

    if (routes instanceof Function) {
      dirty = true
      routes = new routes() // eslint-disable-line new-cap
      routes.server = this
      routes.hapi = this.hapi
    }

    if (routes.routes instanceof Function) {
      dirty = true
      routes = await routes.routes(this.bak)
    }

    if (!routes) {
      routes = []
    }

    if (!(routes instanceof Array)) {
      routes = [routes]
    }

    if (dirty) {
      routes = await parallel(routes, this._loadRoutes.bind(this))
      routes = Array.prototype.concat.apply([], routes)
    }

    return routes
  }

  /**
     *
     * @param routes
     * @param prefix
     * @returns {Promise<Array>}
     * @private
     */
  async _resolveRoutes (routes, prefix = '') {
    routes = await this._loadRoutes(routes, true)
    prefix = ((prefix && prefix.length) ? prefix : '') + '/'

    return Array.prototype.concat.apply([], await parallel(routes, async route => {
      // Simple nested routes
      if (route instanceof Array) {
        return this._resolveRoutes(route, prefix)
      }

      // Prefixed nested routes
      if (route instanceof Object && route.prefix) {
        const nestedPrefix = prefix + route.prefix
        return this._resolveRoutes(route.routes, nestedPrefix)
      }

      // Same level routes
      if (prefix.length) {
        route.path = normalizePath(prefix + (route.path || ''))
      }
      if (!route.path || !route.path.length) {
        route.path = '/'
      }
      if (!route.method) {
        route.method = 'GET'
      }

      return route
    }))
  }

  /**
     *
     * @param routes
     * @returns {Promise}
     */
  async registerRoutes (routes) {
    // Resolve
    routes = await this._resolveRoutes(routes, this.options.prefix)

    // Register
    return this.hapi.route(routes)
  }

  /**
     *
     * @param registration
     * @param registrationOptions
     * @returns {Promise}
     */
  register (registration, registrationOptions) {
    // Normalize registration into { plugin, [options], ... }
    if (!registration.plugin) {
      registration = {
        plugin: registration
      }
    }

    if (!registration.options) {
      registration.options = {}
    }

    // Resolve plugin
    if (typeof registration.plugin === 'string') {
      let path = registration.plugin
      if (this.options.relativeTo && path[0] === '.') {
        path = resolve(this.options.relativeTo, path)
      }
      registration.plugin = require(path)
    }

    // Add _bak to the options
    registration.options._bak = this.bak

    // Resolve and merge configs for plugin
    const configKey = (registration.plugin.configKey || registration.plugin.name || (registration.plugin.pkg && registration.plugin.pkg.name) || '')
      .replace('@bakjs/', '')
      .replace('bak-', '')

    if (configKey.length) {
      // 1. from bak.config.js
      if (this.options[configKey]) {
        Object.assign(registration.options, this.options[configKey])
      }
      // 2. From config/
      if (config.has(configKey)) {
        Object.assign(registration.options, config.get(configKey))
      }
    }

    return this.hapi.register(registration, registrationOptions || {})
  }

  /**
   * Log error to hapi
   * @param {*} error
   * @param {*} request
   */
  logError (error, request = {}) {
    // Ignore Boom or null exceptions
    if (!error || error.isBoom) {
      return
    }

    const tags = [
      'error'
    ]

    const context = {
      error,
      payload: request.payload,
      query: request.query,
      user: request.auth.credentials
    }

    this.hapi.log(tags, context)
  }
}
<commit_msg>remove extra log<commit_after>const config = require('config')
const Hapi = require('hapi')
const { resolve } = require('path')
const { serial, parallel } = require('items-promise')
const { normalizePath } = require('./utils')

const RouteTablePlugin = require('@bakjs/route-table')
const ShortcutsPlugin = require('@bakjs/shortcuts')
const InputPlugin = require('@bakjs/input')
const CompatPlugin = require('@bakjs/compat')

module.exports = class Server {
  /**
     *
     * @param bak
     */
  constructor (bak) {
    this.bak = bak
    this.options = this.bak.options
  }

  /**
     *
     * @returns {Promise.<Server>}
     */
  async init () {
    // Create server
    this.hapi = new Hapi.Server(this.options.server)

    // Register core plugins
    const corePlugins = [
      CompatPlugin,
      ShortcutsPlugin,
      InputPlugin
    ]

    if (this.options.routeTable) {
      corePlugins.push(RouteTablePlugin)
    }

    await serial(corePlugins, this.register.bind(this))

    // Register plugins
    await serial(this.options.registrations, this.register.bind(this))

    // Register routes
    await this.registerRoutes(this.options.routes)

    return this
  }

  /**
     * Start server
     * @returns {Promise}
     * @private
     */
  async start () {
    await this.hapi.initialize()
    await this.hapi.start()
    return this
  }

  /**
     *
     * @param routes
     * @returns {Promise}
     * @private
     */
  async _loadRoutes (routes, dirty = false) {
    if (typeof routes === 'string') {
      dirty = true
      routes = require(resolve(this.options.relativeTo, routes))
    }

    if (routes instanceof Function) {
      dirty = true
      routes = new routes() // eslint-disable-line new-cap
      routes.server = this
      routes.hapi = this.hapi
    }

    if (routes.routes instanceof Function) {
      dirty = true
      routes = await routes.routes(this.bak)
    }

    if (!routes) {
      routes = []
    }

    if (!(routes instanceof Array)) {
      routes = [routes]
    }

    if (dirty) {
      routes = await parallel(routes, this._loadRoutes.bind(this))
      routes = Array.prototype.concat.apply([], routes)
    }

    return routes
  }

  /**
     *
     * @param routes
     * @param prefix
     * @returns {Promise<Array>}
     * @private
     */
  async _resolveRoutes (routes, prefix = '') {
    routes = await this._loadRoutes(routes, true)
    prefix = ((prefix && prefix.length) ? prefix : '') + '/'

    return Array.prototype.concat.apply([], await parallel(routes, async route => {
      // Simple nested routes
      if (route instanceof Array) {
        return this._resolveRoutes(route, prefix)
      }

      // Prefixed nested routes
      if (route instanceof Object && route.prefix) {
        const nestedPrefix = prefix + route.prefix
        return this._resolveRoutes(route.routes, nestedPrefix)
      }

      // Same level routes
      if (prefix.length) {
        route.path = normalizePath(prefix + (route.path || ''))
      }
      if (!route.path || !route.path.length) {
        route.path = '/'
      }
      if (!route.method) {
        route.method = 'GET'
      }

      return route
    }))
  }

  /**
     *
     * @param routes
     * @returns {Promise}
     */
  async registerRoutes (routes) {
    // Resolve
    routes = await this._resolveRoutes(routes, this.options.prefix)

    // Register
    return this.hapi.route(routes)
  }

  /**
     *
     * @param registration
     * @param registrationOptions
     * @returns {Promise}
     */
  register (registration, registrationOptions) {
    // Normalize registration into { plugin, [options], ... }
    if (!registration.plugin) {
      registration = {
        plugin: registration
      }
    }

    if (!registration.options) {
      registration.options = {}
    }

    // Resolve plugin
    if (typeof registration.plugin === 'string') {
      let path = registration.plugin
      if (this.options.relativeTo && path[0] === '.') {
        path = resolve(this.options.relativeTo, path)
      }
      registration.plugin = require(path)
    }

    // Add _bak to the options
    registration.options._bak = this.bak

    // Resolve and merge configs for plugin
    const configKey = (registration.plugin.configKey || registration.plugin.name || (registration.plugin.pkg && registration.plugin.pkg.name) || '')
      .replace('@bakjs/', '')
      .replace('bak-', '')

    if (configKey.length) {
      // 1. from bak.config.js
      if (this.options[configKey]) {
        Object.assign(registration.options, this.options[configKey])
      }
      // 2. From config/
      if (config.has(configKey)) {
        Object.assign(registration.options, config.get(configKey))
      }
    }

    return this.hapi.register(registration, registrationOptions || {})
  }

  /**
   * Log error to hapi
   * @param {*} error
   * @param {*} request
   */
  logError (error, request = {}) {
    // Ignore Boom or null exceptions
    if (!error || error.isBoom) {
      return
    }

    const tags = [
      'error'
    ]

    const context = {
      error,
      payload: request.payload,
      query: request.query,
      user: request.auth.credentials
    }

    this.hapi.log(tags, context)
  }
}
<|endoftext|>


<commit_before>Counter = function (name, cursor, interval) {
  this.name = name;
  this.cursor = cursor;
  this.interval = interval || 1000 * 10;
  this._collectionName = 'counters-collection';
}

// every cursor must provide a collection name via this method
Counter.prototype._getCollectionName = function() {
  return "counter-" + this.name;
};

// the api to publish
Counter.prototype._publishCursor = function(sub) {
  var self = this;
  var count = self.cursor.count();
  sub.added(self._collectionName, self.name, {count: count});

  var handler = Meteor.setInterval(function() {
    var count = self.cursor.count();
    sub.changed(self._collectionName, self.name, {count: count});
  }, this.interval);

  sub.onStop(function() {
    Meteor.clearTimeout(handler);
  });
};

<commit_msg>Make _publishCursor return a handle with a stop method<commit_after>Counter = function (name, cursor, interval) {
  this.name = name;
  this.cursor = cursor;
  this.interval = interval || 1000 * 10;
  this._collectionName = 'counters-collection';
}

// every cursor must provide a collection name via this method
Counter.prototype._getCollectionName = function() {
  return "counter-" + this.name;
};

// the api to publish
Counter.prototype._publishCursor = function(sub) {
  var self = this;
  var count = self.cursor.count();
  sub.added(self._collectionName, self.name, {count: count});

  var handler = Meteor.setInterval(function() {
    var count = self.cursor.count();
    sub.changed(self._collectionName, self.name, {count: count});
  }, this.interval);

  sub.onStop(function() {
    Meteor.clearTimeout(handler);
  });

  return {
    stop: sub.onStop.bind(sub)
  }
};

<|endoftext|>


<commit_before>var express = require('express')
  , utils   = require('./utils')
  , Model   = require('./model')
  , server

exports.reset = function() {
  server = express()
  server.use(express.bodyParser())
  server.use(express.methodOverride())
  server.get('/', function(req, res) {
    res.send(204)
  })
}

exports.reset()

exports.defineAPI = function(model, method, opts) {
  var path = '/' + model._type.toLowerCase()
  if (method !== 'post' && method !== 'view') path += '/:id'
  if (typeof method === 'object') {
    if (method.opts.serverOnly === true) return

    if ('at' in method.opts) path = method.opts.at
    else path += '/' + method.view.toLowerCase()
  }

  if (opts.scope) {
    throw new Error('opts.scope is not support anymore. use opts.middleware instead')
    var scope = exports.server.scope[opts.scope]
  }

  var middleware = []
  if (opts.middleware) {
    middleware = Array.isArray(opts.middleware) ? opts.middleware : [opts.middleware]
  }

  var fn = exports.getMethodFn(typeof method === 'object' ? 'view' : method, model, true)

  server[typeof method === 'object' || method === 'view' ? 'get' : method].apply(server,
  [path].concat(middleware, function(req, res) {
    var _fn = fn
    if (method === 'get' || method === 'put' || method === 'delete')
      _fn = _fn.bind(_fn, req.params.id)
    if (method === 'put' || method === 'post')
      _fn = _fn.bind(_fn, req.body)
    else if (method === 'view')
      _fn = _fn.bind(_fn, req.query.view, req.query.key ? decodeURIComponent(req.query.key) : undefined)
    else if (typeof method === 'object')
      _fn = _fn.bind(_fn, method.view, req.params.id ? decodeURIComponent(req.params.id) : undefined)
    _fn(function(err, result) {
      if (err) {
        if (!err.status) throw err
        if (!err.body) res.send(err.status)
        else res.send(err.status, err.body)
        return
      }

      if (!result) return res.send(404) // Not Found
      else if (method === 'delete') return res.send(204) // No Content
      res.json(Array.isArray(result) ? result.map(function(obj) {
        return obj.toJSON(false, method)
      }) : (typeof result.toJSON === 'function' ? result.toJSON() : result))
    })
  }))
}

var domain = require('domain')
exports.middleware = function() {
  return function(req, res, next) {
    if (!process.domain) {
      var d = domain.create()
      d.enter()
    }
    process.domain.req = req
    server.handle(req, res, next)
  }
}

// generic API Method
exports.getMethodFn = function(operation, model, isAPICall) {
  if (isAPICall === undefined) isAPICall = false
  return function() {
    // initialize parameters
    var args = Array.prototype.slice.call(arguments)

    if (!model._definition.api)
      throw new Error('API for ' + model._type + ' not defined')

    var method   = operation
      , callback = args.pop()
      , isRead   = method === 'get' || method === 'view'
      , isWrite  = !isRead
      , id

    // initialize cache
    if (!process.domain.swac) process.domain.swac = { cache: { docs: {}, views: {} }}
    var cache = process.domain.swac.cache
    if (!cache.views[model._type]) cache.views[model._type] = {}

    // else Server-Side (Authorization required)
    utils
    // Fetch Document(s)
    .chain(function(done) {
      var input = args[0]

      // Not neccessary for post requests
      if (method === 'post') {
        var instance = new model
        if ('id' in input) instance.id = input.id
        return done(input, instance)
      }

      // Not neccessary if the input is already an appropriated instance
      if (isWrite && input instanceof model) {
        // Per-Property && Server-Side!?
        // return done(input, input)
        id = input.id
      }

      // `get`, `delete` will and `put` could have the id
      // as the first argument
      if (typeof input !== 'object') {
        id = input
        // if `put` got called with an id, the second argument
        // is the object containing the changes
        if (method === 'put')
          input = args[1]
      }

      // request the database using the model's adapter
      if (method === 'view') {
        var viewId = args.join('/') || 'all'
          , viewCache = cache.views[model._type]

        if (viewId in viewCache) {
          // use #concat() to copy the array
          return callback(null, [].concat(viewCache[viewId]))
        }

        model._definition.api.view.apply(
          model._definition.api,
          args.concat(function(err, rows) {
            if (err) throw err
            viewCache[viewId] = rows.map(function(row) {
              if (row instanceof model) row = row.clone()
              cache.docs[row.id] = row
              return row
            })
            done(input, rows)
          })
        )
      } else {
        if (id in cache.docs) {
          return done(input, cache.docs[id] ? cache.docs[id].clone() : null)
        }

        model._definition.api.get(id, function(err, row) {
          if (err) throw err
          cache.docs[id] = row ? row.clone() : null
          done(input, row)
        })
      }
    })
    // Authorize Write
    .chain(function (input, instance, done) {
      // Not neccessary for read requests
      if (isRead) return done(instance)
      if (method !== 'delete' && typeof input !== 'object')
        return done(instance)
      if (method === 'put' && !instance) method = 'post'
      else if(method === 'delete' && !instance) {
        return callback({ message: 'Not Found', status: 404, body: {}})
      }

      var data = input instanceof model ? input.toJSON(false, undefined, isAPICall) : input
      if (method === 'put')
        delete data.id

      if (!(input instanceof model))
        input = method === 'post' ? new model(input) : instance

      // the appropriated allow and deny method
      var allow = model._definition._allow.instance[method] || model._definition._allow.instance.write
        || model._definition._allow.instance.all || function() { return true }
      var deny = model._definition._deny.instance[method] || model._definition._deny.instance.write
        || model._definition._deny.instance.all || function() { return false }

      authorize(model, method, allow, deny,
        //allowed
        function() {
          utils
          // Per-Property Authorization
          .chain(function(done) {
            // not neccessary for `delete` requests
            if (method === 'delete') {
              input = instance
              return done()
            }

            // iterate properties
            utils.series(Object.keys(data), function(key, next) {
              // the appropriated allow and deny method
              var allow = model._definition._allow.properties[key]
                && (model._definition._allow.properties[key][method]
                || model._definition._allow.properties[key].write
                || model._definition._allow.properties[key].all)
                || function() { return true }
              var deny = model._definition._deny.properties[key]
                && (model._definition._deny.properties[key][method]
                || model._definition._deny.properties[key].write
                || model._definition._deny.properties[key].all)
                || function() { return false }

              if (
                // model does not have such a property
                !model.prototype._validation[key] ||
                // is serverOnly Property and got accessed through the Web Service
                (isAPICall === true && model.prototype._validation[key].serverOnly)
              ) {
                delete data[key]
                return next()
              }

              // authorize property
              authorize(model, method, allow, deny,
                // allowed
                next,
                // denied
                function() {
                  data[key] = instance[key]
                  next()
                }
              )(method === 'post' ? input : instance, data[key], key)
            }, done)
          })
          // Hooks
          .chain(function(done) {
            if (method !== 'delete') {
              input.set(data)
              input.isNew = false
            }

            utils
            // Before Hooks
            .chain(function(done) {
              var hook
              switch (method) {
                case 'delete': hook = 'beforeDelete'; break
                case 'put':    hook = 'beforeUpdate'; break
                case 'post':   hook = 'beforeCreate'; break
              }
              if (!hook) return done()
              executeHook(model, input, 'beforeSave', function(err) {
                if (err) return callback({ message: 'Bad Request', status: 400, body: {
                  error: 'Save Rejected',
                  details: err
                } })
                executeHook(model, input, hook, function(err) {
                  if (err) return callback({ message: 'Bad Request', status: 400, body: {
                    error: method.charAt(0).toUpperCase() + method.slice(1) + ' Rejected',
                    details: err
                  } })
                  done()
                })
              })
            })
            // Validation
            .chain(function(done) {
              if (method === 'delete') return done()
              input.validate(function(isValid) {
                if (!isValid) {
                  if (callback) {
                    callback({ message: 'Bad Request', status: 400, body: {
                      error: 'Validation Error',
                      details: input.$errors
                    }})
                  }
                } else {
                  done()
                }
              })
            })
            // Execute Write
            .chain(function(done) {
              model._definition.api[method](input, function(err) {
                if (err) throw err
                if (method === 'post' || method === 'delete') {
                  // invalidate views
                  cache.views[model._type] = {}
                }
                // cache document
                if (method !== 'delete') {
                  cache.docs[input.id] = input.clone()
                } else {
                  delete cache.docs[input.id]
                }
                done()
              })
            })
            // After Hooks
            .chain(function() {
              var hook
              switch (method) {
                case 'delete': hook = 'afterDelete'; break
                case 'put':    hook = 'afterUpdate'; break
                case 'post':   hook = 'afterCreate'; break
              }
              if (!hook) return done(input)
              executeHook(model, input, 'afterSave', function() {
                executeHook(model, input, hook, function() {
                  done(input)
                })
              })
            })
          })
          // continue
          .chain(done)
        },
        // denied
        function() {
          if (callback) callback({ message: 'Forbidden', status: 403 })
        },
        isAPICall
      )(method === 'post' ? input : instance)
    })
    // Authorize Read
    .chain(function(rows) {
      if (rows === null) return callback(null, null)
      if (!callback) return
      if (isWrite) return callback(null, rows)

      // the appropriated allow and deny method
      var allow = model._definition._allow.instance[method] || model._definition._allow.instance.read
        || model._definition._allow.instance.all || function() { return true }
      var deny = model._definition._deny.instance[method] || model._definition._deny.instance.read
        || model._definition._deny.instance.all || function() { return false }

      var input = Array.isArray(rows) ? rows : [rows]
        , output = []
      // authorize every row
      utils.series(input, function(row, next) {
        authorize(model, method, allow, deny,
          // allowed
          function() {
            output.push(row)
            next()
          },
          // denied
          next
        )(row)
      }, function() {
        callback(null, Array.isArray(rows) ? output : (output[0] || null))
      })
    })
  }
}

function executeHook(model, obj, hook, callback) {
  if (!model._definition.hooks[hook]) return callback()
  model._definition.hooks[hook](process.domain.req, obj, callback)
}

function authorize(model, method, allowFn, denyFn, allowed, denied, isAPICall) {
  var authorizationContext = {}
  authorizationContext.isClient = isAPICall === true
  authorizationContext.isServer = !authorizationContext.isClient

  return function() {
    var args = Array.prototype.slice.call(arguments)
      , count = 2
      , allow, deny

    var callback = function() {
      if (--count !== 0) return
      if (allow && !deny) allowed.apply(null, args)
      else denied.apply(null, args)
    }

    if (typeof (allow = allowFn.apply(authorizationContext, [process.domain.req].concat(args, function(res) {
      process.nextTick(function() {
        allow = res
        callback()
      })
    }))) !== 'undefined') callback()

    if (typeof (deny = denyFn.apply(authorizationContext, [process.domain.req].concat(args, function(res) {
      process.nextTick(function() {
        deny = res
        callback()
      })
    }))) !== 'undefined') callback()
  }
}<commit_msg>fix view handling for views returning a single model isntead of an array<commit_after>var express = require('express')
  , utils   = require('./utils')
  , Model   = require('./model')
  , server

exports.reset = function() {
  server = express()
  server.use(express.bodyParser())
  server.use(express.methodOverride())
  server.get('/', function(req, res) {
    res.send(204)
  })
}

exports.reset()

exports.defineAPI = function(model, method, opts) {
  var path = '/' + model._type.toLowerCase()
  if (typeof method === 'object') {
    if (method.opts.serverOnly === true) return

    if ('at' in method.opts) path = method.opts.at
    else path += '/' + method.view.toLowerCase() + '/:id'
  } else if (method !== 'post' && method !== 'view') {
    path += '/:id'
  }

  if (opts.scope) {
    throw new Error('opts.scope is not support anymore. use opts.middleware instead')
    var scope = exports.server.scope[opts.scope]
  }

  var middleware = []
  if (opts.middleware) {
    middleware = Array.isArray(opts.middleware) ? opts.middleware : [opts.middleware]
  }

  var fn = exports.getMethodFn(typeof method === 'object' ? 'view' : method, model, true)

  server[typeof method === 'object' || method === 'view' ? 'get' : method].apply(server,
  [path].concat(middleware, function(req, res) {
    var _fn = fn
    if (method === 'get' || method === 'put' || method === 'delete')
      _fn = _fn.bind(_fn, req.params.id)
    if (method === 'put' || method === 'post')
      _fn = _fn.bind(_fn, req.body)
    else if (method === 'view')
      _fn = _fn.bind(_fn, req.query.view, req.query.key ? decodeURIComponent(req.query.key) : undefined)
    else if (typeof method === 'object')
      _fn = _fn.bind(_fn, method.view, req.params.id ? decodeURIComponent(req.params.id) : undefined)
    _fn(function(err, result) {
      if (err) {
        if (!err.status) throw err
        if (!err.body) res.send(err.status)
        else res.send(err.status, err.body)
        return
      }

      if (!result) return res.send(404) // Not Found
      else if (method === 'delete') return res.send(204) // No Content
      res.json(Array.isArray(result) ? result.map(function(obj) {
        return obj.toJSON(false, method)
      }) : (typeof result.toJSON === 'function' ? result.toJSON() : result))
    })
  }))
}

var domain = require('domain')
exports.middleware = function() {
  return function(req, res, next) {
    if (!process.domain) {
      var d = domain.create()
      d.enter()
    }
    process.domain.req = req
    server.handle(req, res, next)
  }
}

// generic API Method
exports.getMethodFn = function(operation, model, isAPICall) {
  if (isAPICall === undefined) isAPICall = false
  return function() {
    if (!process.domain) {
      var d = domain.create()
      d.enter()
    }

    // initialize parameters
    var args = Array.prototype.slice.call(arguments)

    if (!model._definition.api)
      throw new Error('API for ' + model._type + ' not defined')

    var method   = operation
      , callback = args.pop()
      , isRead   = method === 'get' || method === 'view'
      , isWrite  = !isRead
      , id

    // initialize cache
    if (!process.domain.swac) process.domain.swac = { cache: { docs: {}, views: {} }}
    var cache = process.domain.swac.cache
    if (!cache.views[model._type]) cache.views[model._type] = {}

    // else Server-Side (Authorization required)
    utils
    // Fetch Document(s)
    .chain(function(done) {
      var input = args[0]

      // Not neccessary for post requests
      if (method === 'post') {
        var instance = new model
        if ('id' in input) instance.id = input.id
        return done(input, instance)
      }

      // Not neccessary if the input is already an appropriated instance
      if (isWrite && input instanceof model) {
        // Per-Property && Server-Side!?
        // return done(input, input)
        id = input.id
      }

      // `get`, `delete` will and `put` could have the id
      // as the first argument
      if (typeof input !== 'object') {
        id = input
        // if `put` got called with an id, the second argument
        // is the object containing the changes
        if (method === 'put')
          input = args[1]
      }

      function cacheRow(row) {
        if (row) {
          row.isNew = false
          if (row instanceof model) row = row.clone()
          cache.docs[row.id] = row
        }
        return row
      }

      // request the database using the model's adapter
      if (method === 'view') {
        var viewId = args.join('/') || 'all'
          , viewCache = cache.views[model._type]

        if (viewId in viewCache) {
          // use #concat() to copy the array
          var rows = viewCache[viewId]
          return callback(null, Array.isArray(rows)
              ? rows.map(cacheRow)
              : cacheRow(rows))
        }

        model._definition.api.view.apply(
          model._definition.api,
          args.concat(function(err, rows) {
            if (err) throw err
            viewCache[viewId] = Array.isArray(rows)
              ? rows.map(cacheRow)
              : cacheRow(rows)
            done(input, rows)
          })
        )
      } else {
        if (id in cache.docs) {
          return done(input, cacheRow(cache.docs[id]))
        }

        model._definition.api.get(id, function(err, row) {
          if (err) throw err
          done(input, cacheRow(row))
        })
      }
    })
    // Authorize Write
    .chain(function (input, instance, done) {
      // Not neccessary for read requests
      if (isRead) return done(instance)
      if (method !== 'delete' && typeof input !== 'object')
        return done(instance)
      if (method === 'put' && !instance) method = 'post'
      else if(method === 'delete' && !instance) {
        return callback({ message: 'Not Found', status: 404, body: {}})
      }

      var data = input instanceof model ? input.toJSON(false, undefined, isAPICall) : input
      if (method === 'put')
        delete data.id

      if (!(input instanceof model))
        input = method === 'post' ? new model(input) : instance

      // the appropriated allow and deny method
      var allow = model._definition._allow.instance[method] || model._definition._allow.instance.write
        || model._definition._allow.instance.all || function() { return true }
      var deny = model._definition._deny.instance[method] || model._definition._deny.instance.write
        || model._definition._deny.instance.all || function() { return false }

      authorize(model, method, allow, deny,
        //allowed
        function() {
          utils
          // Per-Property Authorization
          .chain(function(done) {
            // not neccessary for `delete` requests
            if (method === 'delete') {
              input = instance
              return done()
            }

            // iterate properties
            utils.series(Object.keys(data), function(key, next) {
              // the appropriated allow and deny method
              var allow = model._definition._allow.properties[key]
                && (model._definition._allow.properties[key][method]
                || model._definition._allow.properties[key].write
                || model._definition._allow.properties[key].all)
                || function() { return true }
              var deny = model._definition._deny.properties[key]
                && (model._definition._deny.properties[key][method]
                || model._definition._deny.properties[key].write
                || model._definition._deny.properties[key].all)
                || function() { return false }

              if (
                // model does not have such a property
                !model.prototype._validation[key] ||
                // is serverOnly Property and got accessed through the Web Service
                (isAPICall === true && model.prototype._validation[key].serverOnly)
              ) {
                delete data[key]
                return next()
              }

              // authorize property
              authorize(model, method, allow, deny,
                // allowed
                next,
                // denied
                function() {
                  data[key] = instance[key]
                  next()
                },
                isAPICall
              )(method === 'post' ? input : instance, data[key], key)
            }, done)
          })
          // Hooks
          .chain(function(done) {
            if (method !== 'delete') {
              input.set(data)
              input.isNew = false
            }

            utils
            // Before Hooks
            .chain(function(done) {
              var hook
              switch (method) {
                case 'delete': hook = 'beforeDelete'; break
                case 'put':    hook = 'beforeUpdate'; break
                case 'post':   hook = 'beforeCreate'; break
              }
              if (!hook) return done()
              executeHook(model, input, 'beforeSave', function(err) {
                if (err) return callback({ message: 'Bad Request', status: 400, body: {
                  error: 'Save Rejected',
                  details: err
                } })
                executeHook(model, input, hook, function(err) {
                  if (err) return callback({ message: 'Bad Request', status: 400, body: {
                    error: method.charAt(0).toUpperCase() + method.slice(1) + ' Rejected',
                    details: err
                  } })
                  done()
                })
              })
            })
            // Validation
            .chain(function(done) {
              if (method === 'delete') return done()
              input.validate(function(isValid) {
                if (!isValid) {
                  if (callback) {
                    callback({ message: 'Bad Request', status: 400, body: {
                      error: 'Validation Error',
                      details: input.$errors
                    }})
                  }
                } else {
                  done()
                }
              })
            })
            // Execute Write
            .chain(function(done) {
              model._definition.api[method](input, function(err) {
                if (err) throw err
                if (method === 'post' || method === 'delete') {
                  // invalidate views
                  cache.views[model._type] = {}
                }
                // cache document
                if (method !== 'delete') {
                  cache.docs[input.id] = input.clone()
                } else {
                  delete cache.docs[input.id]
                }
                done()
              })
            })
            // After Hooks
            .chain(function() {
              var hook
              switch (method) {
                case 'delete': hook = 'afterDelete'; break
                case 'put':    hook = 'afterUpdate'; break
                case 'post':   hook = 'afterCreate'; break
              }
              if (!hook) return done(input)
              executeHook(model, input, 'afterSave', function() {
                executeHook(model, input, hook, function() {
                  done(input)
                })
              })
            })
          })
          // continue
          .chain(done)
        },
        // denied
        function() {
          if (callback) callback({ message: 'Forbidden', status: 403 })
        },
        isAPICall
      )(method === 'post' ? input : instance)
    })
    // Authorize Read
    .chain(function(rows) {
      if (rows === null) return callback(null, null)
      if (!callback) return
      if (isWrite) return callback(null, rows)

      if (method === 'view' && !Array.isArray(rows)) {
        method = 'get'
      }

      // the appropriated allow and deny method
      var allow = model._definition._allow.instance[method] || model._definition._allow.instance.read
        || model._definition._allow.instance.all || function() { return true }
      var deny = model._definition._deny.instance[method] || model._definition._deny.instance.read
        || model._definition._deny.instance.all || function() { return false }

      var input = Array.isArray(rows) ? rows : [rows]
        , output = []
      // authorize every row
      utils.series(input, function(row, next) {
        authorize(model, method, allow, deny,
          // allowed
          function() {
            output.push(row)
            next()
          },
          // denied
          next,
          isAPICall
        )(row)
      }, function() {
        callback(null, Array.isArray(rows) ? output : (output[0] || null))
      }, isAPICall)
    })
  }
}

function executeHook(model, obj, hook, callback) {
  if (!model._definition.hooks[hook]) return callback()
  model._definition.hooks[hook](process.domain.req, obj, callback)
}

function authorize(model, method, allowFn, denyFn, allowed, denied, isAPICall) {
  var authorizationContext = {}
  authorizationContext.isClient = isAPICall === true
  authorizationContext.isServer = !authorizationContext.isClient

  return function() {
    var args = Array.prototype.slice.call(arguments)
      , count = 2
      , allow, deny

    var callback = function() {
      if (--count !== 0) return
      if (allow && !deny) allowed.apply(null, args)
      else denied.apply(null, args)
    }

    if (typeof (allow = allowFn.apply(authorizationContext, [process.domain.req].concat(args, function(res) {
      process.nextTick(function() {
        allow = res
        callback()
      })
    }))) === 'boolean' || allowFn.length <= (args.length + 1)) callback()

    if (typeof (deny = denyFn.apply(authorizationContext, [process.domain.req].concat(args, function(res) {
      process.nextTick(function() {
        deny = res
        callback()
      })
    }))) === 'boolean' || denyFn.length <= (args.length + 1)) callback()
  }
}<|endoftext|>


<commit_before>var http = require('http');
var url = require('url');

var plugin = require('./plugin.js').handle;
var middleware = require('./middleware.js').handle;

var server = {
	start: function(config) {
		if(config)
			this.configure(config);

		// Initialize plugins and start the server
		if(Object.keys(this.plugins).length > 0) {
			this.log.debug('Initializing plugins.');
			plugin.call(this, function() {
				this.log.debug('All plugins initialized.');
				server.listen.call(this);
			});
		}
		else
			server.listen.call(this);
		return this;
	},

	listen: function() {
		this.log.info('Starting server at http://' + this.config.host + ":" + this.config.port)

		var self = this;
		this.server = http.createServer(function(request, response) {

			// Find a matching route
			var route, match;
			for(var i=0;i<self.routes.length;i++) {
				match = self.routes[i].re.exec(url.parse(request.url).pathname);
				if(match) {
					if(self.routes[i].methods.indexOf(request.method)) {
						if(self.routes[i].methods.length > 0)
							continue;
					}
					route = self.routes[i];
					break;
				}
			}

			self.log.info('[%s] %s', request.method, url.parse(request.url).pathname)

			middleware.call(self, route, request, response, function(err) {
				if(err) {
					response.status(500);
					if(self.config.env == 'development')
						response.send(err.stack);
					else
						response.send(err.toString());
				} else {
					if(route) {
						if(route.keys.length == 0)
							request.params = match;
						else {
							request.params = {}
							for(var i=0;i<route.keys.length;i++)
								request.params[route.keys[i].name] = match[i+1];
						}

						if(route.handler)
							route.handler(request, response);
					}
					else {
						response.status(404).send('404');
					}
				}
			});
		});
		this.server.listen(this.config.port, this.config.host);
	}
}

module.exports = server.start<commit_msg>Use route's matching logic rather than implement it inside listen function.<commit_after>var http = require('http');
var url = require('url');

var plugin = require('./plugin.js').handle;
var middleware = require('./middleware.js').handle;

var server = {
	start: function(config) {
		if(config)
			this.configure(config);

		// Initialize plugins and start the server
		if(Object.keys(this.plugins).length > 0) {
			this.log.debug('Initializing plugins.');
			plugin.call(this, function() {
				this.log.debug('All plugins initialized.');
				server.listen.call(this);
			});
		}
		else
			server.listen.call(this);
		return this;
	},

	listen: function() {
		this.log.info('Starting server at http://' + this.config.host + ":" + this.config.port)

		var self = this;
		this.server = http.createServer(function(request, response) {

			// Find a matching route
			var route, match;
			for(var i=0;i<self.routes.length;i++) {

				match = self.routes[i].match(url.parse(request.url).pathname, request.method);
				if(match) {
					// Match found, no need to loop any further
					route = match.route;
					break;
				}
			}

			self.log.info('[%s] %s', request.method, url.parse(request.url).pathname)

			middleware.call(self, route, request, response, function(err) {
				if(err) {
					response.status(500);
					if(self.config.env == 'development')
						response.send(err.stack);
					else
						response.send(err.toString());
				} else {
					if(route && match) {

						request.params = match.params;
						request.splats = match.splats;

						if(route.handler) {
							route.handler(request, response);
						}
					}
					else {
						response.status(404).send('404');
					}
				}
			});
		});
		this.server.listen(this.config.port, this.config.host);
	}
}

module.exports = server.start<|endoftext|>


<commit_before>var io = require('socket.io');
var net = require('net');
var cfg = require('./config');
var ws = require('./web-server');
var logger = require('./logger');
var browser = require('./browser');
var reporter = require('./reporter');
var events = require('./events');
var util = require('./util');
var constant = require('./constants');
var Launcher = require('./launcher').Launcher;
var FileList = require('./file-list').List;
var watcher = require('./watcher');

// TODO(vojta): get this whole mess under test
exports.start = function(cliOptions) {
  var config = cfg.parseConfig(cliOptions.configFile, cliOptions);

  logger.setLevel(config.logLevel);
  logger.useColors(config.colors);

  var log = logger.create();
  var globalEmitter = new events.EventEmitter();
  var launcher = new Launcher();

  var fileList = new FileList(config.files, config.exclude, globalEmitter);
  // TODO(vojta): wait for fileList resolving before serving first request...
  fileList.refresh(function() {
    if (config.autoWatch) {
      watcher.watch(config.files, fileList);
    }
  });

  var webServer = ws.createWebServer(fileList, config.basePath);
  var socketServer = io.listen(webServer, {
    logger: logger.create('socket.io', 0),
    transports: ['websocket', 'xhr-polling', 'jsonp-polling']
  });

  webServer.on('error', function(e) {
    if (e.code === 'EADDRINUSE') {
      log.warn('Port %d in use', config.port);
      config.port++;
      webServer.listen(config.port);
    } else {
      throw e;
    }
  });

  webServer.listen(config.port, function() {
    log.info('Web server started at http://localhost:' + config.port);

    if (config.browsers && config.browsers.length) {
      launcher.launch(config.browsers, config.port);
    }
  });

  var resultReporter = reporter.createReporter(config.reporter, config.colors, config.basePath);
  globalEmitter.bind(resultReporter);

  var capturedBrowsers = new browser.Collection(globalEmitter);
  var executionScheduled = false;
  var pendingCount = 0;
  var runningBrowsers;

  globalEmitter.on('browsers_change', function() {
    // TODO(vojta): send only to interested browsers
    socketServer.sockets.emit('info', capturedBrowsers.serialize());
  });

  globalEmitter.on('browser_register', function(browser) {
    if (browser.launchId) {
      launcher.markCaptured(browser.launchId);
    }

    if (config.singleRun && launcher.areAllCaptured()) {
      tryExecution();
    }
  });

  var tryExecution = function() {
    var nonReady = [];

    if (!capturedBrowsers.length) {
      log.warn('No captured browser, open http://localhost:' + config.port);
      return false;
    } else if (capturedBrowsers.areAllReady(nonReady)) {
      log.debug('All browsers are ready, executing');
      executionScheduled = false;
      capturedBrowsers.setAllIsReadyTo(false);
      capturedBrowsers.clearResults();
      pendingCount = capturedBrowsers.length;
      runningBrowsers = capturedBrowsers.toArray();
      globalEmitter.emit('run_start', runningBrowsers);
      socketServer.sockets.emit('execute', {});
      return true;
    } else {
      log.info('Delaying execution, these browsers are not ready: ' + nonReady.join(', '));
      executionScheduled = true;
      return false;
    }
  };

  globalEmitter.on('browser_complete', function(browser) {
    pendingCount--;

    if (!pendingCount) {
      // TODO(vojta): change runningBrowsers to collection and put getResults() there
      globalEmitter.emit('run_complete', runningBrowsers, capturedBrowsers.getResults());
    }
  });

  globalEmitter.on('run_complete', function(browsers) {
    if (config.singleRun) {
        // get the exit code
        // 0 - success (all executed tests passed, no disconnected browser, no error)
        // 1 - otherwise
        // TODO(vojta): move this logic into getResults()
        var code = browsers.reduce(function(code, browser) {
          var lastResults = browser.lastResult;
          return (lastResults.failed || lastResults.disconnected || lastResults.error) ? 1 : code;
        }, 0);

        disconnectBrowsers(code);
    } else if (executionScheduled) {
      tryExecution();
    }
  });

  socketServer.sockets.on('connection', function (socket) {
    log.debug('New browser has connected on socket ' + socket.id);
    browser.createBrowser(socket, capturedBrowsers, globalEmitter);
  });

  globalEmitter.on('file_list_modified', function() {
    log.debug('Execution (fired by autoWatch)');
    tryExecution();
  });

  // listen on port, waiting for runner
  var runnerServer = net.createServer(function (socket) {
    log.debug('Execution (fired by runner)');

    if (!capturedBrowsers.length) {
      log.warn('No captured browser, open http://localhost:' + config.port);
      socket.end('No captured browser, open http://localhost:' + config.port + '\n');
      return;
    }

    // TODO(vojta): this should delay web server (when serving context.html), if necesarry
    // Currently it can happen, that first request will be served before files are resolved
    fileList.refresh();

    globalEmitter.once('run_start', function() {
      var socketWrite = socket.write.bind(socket);

      resultReporter.adapters.push(socketWrite);

      // clean up, close runner socket
      globalEmitter.once('run_complete', function(browsers, results) {
        // get the exit code
        // 0 - success (all executed tests passed, no disconnected browser, no error)
        // 1 - otherwise
        // TODO(vojta): move this logic into getResults()
        var code = browsers.reduce(function(code, browser) {
          var lastResults = browser.lastResult;
          return (lastResults.failed || lastResults.disconnected || lastResults.error) ? 1 : code;
        }, 0);

        util.arrayRemove(resultReporter.adapters, socketWrite);
        socket.end(!code && constant.EXIT_CODE_0);
      });
    });

    if (!tryExecution()) {
      socket.write('Waiting for previous execution...\n');
    }
  });

  runnerServer.on('error', function(e) {
    if (e.code === 'EADDRINUSE') {
      log.warn('Port %d in use', config.runnerPort);
      config.runnerPort++;
      runnerServer.listen(config.runnerPort);
    } else {
      throw e;
    }
  });

  runnerServer.listen(config.runnerPort);

  runnerServer.on('listening', function() {
    if (config.runnerPort !== constant.DEFAULT_RUNNER_PORT) {
      log.info('To run via this server, use "testacular run --runner-port %d"', config.runnerPort);
    }
  });

  var disconnectBrowsers = function(code) {
    log.info('Disconnecting all browsers');
    socketServer.sockets.emit('server_disconnect');

    // Slightly hacky way of removing disconnect listeners
    // to suppress "browser disconnect" warnings
    // TODO(vojta): change the client to not send the event (if disconnected by purpose)
    var sockets = socketServer.sockets.sockets;
    Object.getOwnPropertyNames(sockets).forEach(function(key) {
      sockets[key].removeAllListeners('disconnect');
    });

    log.debug('Waiting for child processes to finish');
    launcher.kill(function() {
      process.exit(code || 0);
    });

    // TODO(vojta): remove this travis-ci hack
    if (process.env.TRAVIS) {
      process.exit(code || 0);
    }
  };

  try {
    process.on('SIGINT', disconnectBrowsers);
    process.on('SIGTERM', disconnectBrowsers);
  } catch (e) {
    // Windows doesn't support signals yet, so they simply don't get this handling.
    // https://github.com/joyent/node/issues/1553
  }
};
<commit_msg>Fix race condition in testacular run<commit_after>var io = require('socket.io');
var net = require('net');
var cfg = require('./config');
var ws = require('./web-server');
var logger = require('./logger');
var browser = require('./browser');
var reporter = require('./reporter');
var events = require('./events');
var util = require('./util');
var constant = require('./constants');
var Launcher = require('./launcher').Launcher;
var FileList = require('./file-list').List;
var watcher = require('./watcher');

// TODO(vojta): get this whole mess under test
exports.start = function(cliOptions) {
  var config = cfg.parseConfig(cliOptions.configFile, cliOptions);

  logger.setLevel(config.logLevel);
  logger.useColors(config.colors);

  var log = logger.create();
  var globalEmitter = new events.EventEmitter();
  var launcher = new Launcher();

  var fileList = new FileList(config.files, config.exclude, globalEmitter);
  // TODO(vojta): wait for fileList resolving before serving first request...
  fileList.refresh(function() {
    if (config.autoWatch) {
      watcher.watch(config.files, fileList);
    }
  });

  var webServer = ws.createWebServer(fileList, config.basePath);
  var socketServer = io.listen(webServer, {
    logger: logger.create('socket.io', 0),
    transports: ['websocket', 'xhr-polling', 'jsonp-polling']
  });

  webServer.on('error', function(e) {
    if (e.code === 'EADDRINUSE') {
      log.warn('Port %d in use', config.port);
      config.port++;
      webServer.listen(config.port);
    } else {
      throw e;
    }
  });

  webServer.listen(config.port, function() {
    log.info('Web server started at http://localhost:' + config.port);

    if (config.browsers && config.browsers.length) {
      launcher.launch(config.browsers, config.port);
    }
  });

  var resultReporter = reporter.createReporter(config.reporter, config.colors, config.basePath);
  globalEmitter.bind(resultReporter);

  var capturedBrowsers = new browser.Collection(globalEmitter);
  var executionScheduled = false;
  var pendingCount = 0;
  var runningBrowsers;

  globalEmitter.on('browsers_change', function() {
    // TODO(vojta): send only to interested browsers
    socketServer.sockets.emit('info', capturedBrowsers.serialize());
  });

  globalEmitter.on('browser_register', function(browser) {
    if (browser.launchId) {
      launcher.markCaptured(browser.launchId);
    }

    if (config.singleRun && launcher.areAllCaptured()) {
      tryExecution();
    }
  });

  var tryExecution = function() {
    var nonReady = [];

    if (!capturedBrowsers.length) {
      log.warn('No captured browser, open http://localhost:' + config.port);
      return false;
    } else if (capturedBrowsers.areAllReady(nonReady)) {
      log.debug('All browsers are ready, executing');
      executionScheduled = false;
      capturedBrowsers.setAllIsReadyTo(false);
      capturedBrowsers.clearResults();
      pendingCount = capturedBrowsers.length;
      runningBrowsers = capturedBrowsers.toArray();
      globalEmitter.emit('run_start', runningBrowsers);
      socketServer.sockets.emit('execute', {});
      return true;
    } else {
      log.info('Delaying execution, these browsers are not ready: ' + nonReady.join(', '));
      executionScheduled = true;
      return false;
    }
  };

  globalEmitter.on('browser_complete', function(browser) {
    pendingCount--;

    if (!pendingCount) {
      // TODO(vojta): change runningBrowsers to collection and put getResults() there
      globalEmitter.emit('run_complete', runningBrowsers, capturedBrowsers.getResults());
    }
  });

  globalEmitter.on('run_complete', function(browsers) {
    if (config.singleRun) {
        // get the exit code
        // 0 - success (all executed tests passed, no disconnected browser, no error)
        // 1 - otherwise
        // TODO(vojta): move this logic into getResults()
        var code = browsers.reduce(function(code, browser) {
          var lastResults = browser.lastResult;
          return (lastResults.failed || lastResults.disconnected || lastResults.error) ? 1 : code;
        }, 0);

        disconnectBrowsers(code);
    } else if (executionScheduled) {
      tryExecution();
    }
  });

  socketServer.sockets.on('connection', function (socket) {
    log.debug('New browser has connected on socket ' + socket.id);
    browser.createBrowser(socket, capturedBrowsers, globalEmitter);
  });

  globalEmitter.on('file_list_modified', function() {
    log.debug('Execution (fired by autoWatch)');
    tryExecution();
  });

  // listen on port, waiting for runner
  var runnerServer = net.createServer(function (socket) {
    log.debug('Execution (fired by runner)');

    if (!capturedBrowsers.length) {
      log.warn('No captured browser, open http://localhost:' + config.port);
      socket.end('No captured browser, open http://localhost:' + config.port + '\n');
      return;
    }

    // TODO(vojta): we should immediately fire run, but delay serving the context.html
    log.debug('Refreshing all the files / patterns');
    fileList.refresh(function() {
      if (!tryExecution()) {
        socket.write('Waiting for previous execution...\n');
      }
    });

    globalEmitter.once('run_start', function() {
      var socketWrite = socket.write.bind(socket);

      resultReporter.adapters.push(socketWrite);

      // clean up, close runner socket
      globalEmitter.once('run_complete', function(browsers, results) {
        // get the exit code
        // 0 - success (all executed tests passed, no disconnected browser, no error)
        // 1 - otherwise
        // TODO(vojta): move this logic into getResults()
        var code = browsers.reduce(function(code, browser) {
          var lastResults = browser.lastResult;
          return (lastResults.failed || lastResults.disconnected || lastResults.error) ? 1 : code;
        }, 0);

        util.arrayRemove(resultReporter.adapters, socketWrite);
        socket.end(!code && constant.EXIT_CODE_0);
      });
    });
  });

  runnerServer.on('error', function(e) {
    if (e.code === 'EADDRINUSE') {
      log.warn('Port %d in use', config.runnerPort);
      config.runnerPort++;
      runnerServer.listen(config.runnerPort);
    } else {
      throw e;
    }
  });

  runnerServer.listen(config.runnerPort);

  runnerServer.on('listening', function() {
    if (config.runnerPort !== constant.DEFAULT_RUNNER_PORT) {
      log.info('To run via this server, use "testacular run --runner-port %d"', config.runnerPort);
    }
  });

  var disconnectBrowsers = function(code) {
    log.info('Disconnecting all browsers');
    socketServer.sockets.emit('server_disconnect');

    // Slightly hacky way of removing disconnect listeners
    // to suppress "browser disconnect" warnings
    // TODO(vojta): change the client to not send the event (if disconnected by purpose)
    var sockets = socketServer.sockets.sockets;
    Object.getOwnPropertyNames(sockets).forEach(function(key) {
      sockets[key].removeAllListeners('disconnect');
    });

    log.debug('Waiting for child processes to finish');
    launcher.kill(function() {
      process.exit(code || 0);
    });

    // TODO(vojta): remove this travis-ci hack
    if (process.env.TRAVIS) {
      process.exit(code || 0);
    }
  };

  try {
    process.on('SIGINT', disconnectBrowsers);
    process.on('SIGTERM', disconnectBrowsers);
  } catch (e) {
    // Windows doesn't support signals yet, so they simply don't get this handling.
    // https://github.com/joyent/node/issues/1553
  }
};
<|endoftext|>


<commit_before>#!/usr/bin/env node

/**
 * An HTTP server to serve classification requests
 *
 * Usage:
 * % export MAPBOX_ACCESS_TOKEN="pk.foo" ./server.js
 * Routes:
 * GET /classify?tile=z/x/y
 *
 * NOTES:
 * - should run inside rodowi/inception_serving container
 * - listens in port 3000
 *
 */

const execFile = require('child_process').execFile
const express = require('express')
const app = express()

const port = 3000
const url = 'https://b.tiles.mapbox.com/v4/mapbox.satellite'
const token = process.env.MAPBOX_ACCESS_TOKEN

app.get('/', function (req, res) {
  var zxy = req.query.tile
  download(zxy, (err, filename) => {
    if (err) return res.send(err)
    predict(filename, (err, label) => {
      if (err) return res.send(err)
      res.send('CAT\n')
    })
  })
})

function download (tile, done) {
  var filename = `${process.env.PWD}/imagery/${tile.replace(/\//g, '-')}.jpg`
  var command = `curl ${url}/${tile}@2x.png?access_token=${token} > ${filename}`
  command = 'curl'
  var params = [
    `${url}/${tile}@2x.png?access_token=${token}`,
    '-o',
    filename
  ]
  execFile(command, params, (err, stdout, stderr) => {
    done(err, filename)
  })
}

function predict (filename, done) {
  var command = '/serving/bazel-bin/tensorflow_serving/example' +
    '/inception_client --server=localhost:9000 ' +
    '--image=' + filename
  execFile(`${command}`, (err, stdout, stderr) => {
    done(err, stderr + stdout)
  })
}

app.listen(port)
console.log('Listening for tile requests in port', port)
<commit_msg>Fix execFile invocation<commit_after>#!/usr/bin/env node

/**
 * An HTTP server to serve classification requests
 *
 * Usage:
 * % export MAPBOX_ACCESS_TOKEN="pk.foo" ./server.js
 * Routes:
 * GET /classify?tile=z/x/y
 *
 * NOTES:
 * - should run inside rodowi/inception_serving container
 * - listens in port 3000
 *
 */

const execFile = require('child_process').execFile
const express = require('express')
const app = express()

const port = 3000
const url = 'https://b.tiles.mapbox.com/v4/mapbox.satellite'
const token = process.env.MAPBOX_ACCESS_TOKEN

app.get('/', function (req, res) {
  var zxy = req.query.tile
  download(zxy, (err, filename) => {
    if (err) return res.send(err)
    predict(filename, (err, label) => {
      if (err) return res.send(err)
      res.send('CAT\n')
    })
  })
})

function download (tile, done) {
  var filename = `${process.env.PWD}/imagery/${tile.replace(/\//g, '-')}.jpg`
  var command = 'curl'
  var params = [
    `${url}/${tile}@2x.png?access_token=${token}`,
    '-o',
    filename
  ]
  execFile(command, params, (err, stdout, stderr) => {
    done(err, filename)
  })
}

function predict (filename, done) {
  var cmd = '/serving/bazel-bin/tensorflow_serving/example/inception_client'
  var params = [
    '--server=localhost:9000',
    '--image=' + filename
  ]
  execFile(cmd, params, (err, stdout, stderr) => {
    done(err, stderr + stdout)
  })
}

app.listen(port)
console.log('Listening for tile requests in port', port)
<|endoftext|>


<commit_before>'use strict'

var express = require('express')
var https = require('https')
var http = require('http')
var auth = require('http-auth')
var io = require('socket.io')
var fs = require('fs')
var faye = require('faye')
var expressWinston = require('express-winston')
var cookieParser = require('cookie-parser')
var favicon = require('serve-favicon')
var bodyParser = require('body-parser')
var expressValidator = require('express-validator')
var compression = require('compression')
var cors = require('cors')
var consolidate = require('consolidate')
var proxiedHttp = require('findhit-proxywrap')

module.exports = function (properties) {
  // Server and Middlewares properties
  if (!properties.ENV) throw new Error('Must have ENV in properties')
  if (!properties.server) throw new Error('Must have server settings in properties')
  var environment = properties.ENV.type
  var https_port = properties.server.https_port
  var http_port = properties.server.http_port
  var sockets = properties.server.sockets
  var faviconDir = properties.server.favicon
  var cookies_secret = properties.server.cookies_secret
  var compression_level = properties.server.compression == null ? 6 : properties.server.compression
  var proxy = properties.server.proxy
  var proxy_strict = properties.server.proxy_strict
  var key_file = properties.ssl && properties.ssl.key
  var crt_file = properties.ssl && properties.ssl.crt
  var mount = properties.faye && properties.faye.mount
  var timeout = properties.faye && properties.faye.timeout
  var engine_type = properties.engine && properties.engine.type || 'jade'
  var file_extantion = properties.engine && properties.engine.file_extantion || '.jade'
  var views_folder = properties.engine && properties.engine.view_folder || 'app/views'
  var static_folder = properties.engine && properties.engine.static_folder
  var admin_users = properties.basic && properties.basic.admin_users
  var realm = properties.basic && properties.basic.realm
  var validator = {customValidators: properties.modules && properties.modules.validator}
  var router = properties.modules && properties.modules.router
  var error = properties.modules && properties.modules.error
  var logger = properties.modules && properties.modules.logger
  // /////////////////////////Building the express server///////////////////////////

  //  Express Server
  var app = express()

  // If proxy procotol is enabled we need to fix standard headers
  if (proxy === 'true') {
    app.all('*', function (req, res, next) {
      req['X-Forwarded-For'] = req.connection.remoteAddress
      req['X-Forwarded-Proto'] = req.connection.proxyPort
      next()
    })
  }

  // Gzip compression middleware
  app.use(compression({level: compression_level}))
  // Adds optional express logging to winston logger
  logger && app.use(expressWinston.logger({
    winstonInstance: logger,
    meta: true,
    colorStatus: true
  }))

  // Adding optional cookies encryption
  if (cookies_secret) {
    app.use(cookieParser(cookies_secret))
    logger.info('Your cookies are now encrypted - :)')
  } else {
    logger.info('You can\' use Cookies - :(')
  }

  // Dev mode middlewares
  if (environment === 'development') {
    // Allow the client to choose HTTP method as a query param
    app.use(function (req, res, next) {
      req.query.method && (req.method = req.query.method)
      next()
    })
    // Adds optional basic http authentication to all routes
    if (admin_users && realm) {
      var basic = auth.basic({
        realm: realm,
        file: admin_users,
        skipUser: true
      })
      app.use(auth.connect(basic))
    }
  }

  app.use(favicon(faviconDir))                            // Fast fav icon middleware
  app.use(cors())                                         // Allowing CORS comunication
  app.use(bodyParser.json())                              // Support for JSON-encoded bodies
  app.use(bodyParser.urlencoded({extended: true}))        // Support for URL-encoded bodies
  app.engine(file_extantion, consolidate[engine_type])    // assign the template engine to the file extantion
  app.set('view engine', file_extantion)                  // set file extantion as the default extension
  app.set('views', views_folder)                          // Changing default view folder
  app.use(expressValidator(validator))                    // Parameter validation middleware
  router && app.use(router)                               // Load the router to the server
  static_folder && app.use(express.static(static_folder)) // Optional HTTP static server middleware
  logger && app.use(expressWinston.errorLogger({          // Adds optional express error logging to winston logger
    winstonInstance: logger,
    level: 'silly',
    statusLevels: true
  }))

  // HTTP status 404 response middlware
  app.use(function (req, res, next) {
    res.status(404)
    res.format({
      html: function () { res.render('404') },              // respond with html page
      json: function () { res.send({error: 'Not found'}) }, // respond with json
      default: function () { res.send('Not found') }        // default to plain-text
    })
  })
  error && app.use(error)

  // ////////////////////Building The returned Server Object//////////////////////
  var server = {
    port: http_port,
    ssl: false,
    pub_sub: false,
    sockets: false
  }
  // Default http server to wrap the express app in
  var http_server = http

  // Adding optional SSL to the server
  if (key_file && crt_file) {
    var ssl_key = fs.readFileSync(key_file)
    var ssl_cert = fs.readFileSync(crt_file)

    if (ssl_key && ssl_cert) {
      // SSL settings
      var options = {
        key: ssl_key,
        cert: ssl_cert,
        ciphers: 'HIGH:!DSS:!aNULL@STRENGTH',
        honorCipherOrder: true
      }
      // Change http_server to be an HTTPS server
      http_server = https
      // Change SSL flag to true
      server.ssl = true
      // Set port to https port
      server.port = https_port
      logger.info('You now have a https server with SSL enabled')
      // set up plain http server
      var https_redirect = express()
      // set up a route to redirect http to https
      https_redirect.use(function (req, res) {
        res.redirect('https://' + req.get('Host') + req.url)
      })
      // have it listen on http to redirect users to https
      https_redirect.listen(http_port)
    } else {
      logger.info('Can\'t find SSL certifcate and key files so no SSL for you')
    }
  } else {
    logger.info('Can\'t find SSL properties so no SSL for you')
  }

  // If proxy protocol in enabled, we will wrap the http_server we have with proxy server
  if (proxy === 'true') {
    http_server = proxiedHttp.proxy(http_server, {strict: proxy_strict})
  }

  // Create the server using whatever http_server we have now
  if (options) server.http_server = http_server.createServer(options, app)
  else server.http_server = http_server.createServer(app)

  // Adding optional Pub/Sub capabilities using Faye
  if (mount && timeout) {
    server.pub_sub = true
    var bayeux = new faye.NodeAdapter({mount: mount, timeout: timeout})
    //  Faye Server
    bayeux.attach(server)
    logger.info('faye started at mount: ' + mount + ', with timeout: ' + timeout)
  } else {
    logger.info('Can\'t find faye properties so no pub/sub for you')
  }

  // Adding optional websockers capabilities using Socket.IO
  if (sockets === 'true') {
    // Sockets Server
    server.io_server = io(server.http_server)
    server.io_server.on('connection', function (socket) {
      server.sockets = true
      logger.info('user conneced to scoket with id: ' + socket.id)
      socket.on('disconnect', function () {
        logger.info('user disconnect from socket')
      })
    })
  }
  return (server)
}
<commit_msg>fixed a potential problem in server when no logger is passed<commit_after>'use strict'

var express = require('express')
var https = require('https')
var http = require('http')
var auth = require('http-auth')
var io = require('socket.io')
var fs = require('fs')
var faye = require('faye')
var expressWinston = require('express-winston')
var cookieParser = require('cookie-parser')
var favicon = require('serve-favicon')
var bodyParser = require('body-parser')
var expressValidator = require('express-validator')
var compression = require('compression')
var cors = require('cors')
var consolidate = require('consolidate')
var proxiedHttp = require('findhit-proxywrap')

module.exports = function (properties) {
  // Server and Middlewares properties
  if (!properties.ENV) throw new Error('Must have ENV in properties')
  if (!properties.server) throw new Error('Must have server settings in properties')
  var environment = properties.ENV.type
  var https_port = properties.server.https_port
  var http_port = properties.server.http_port
  var sockets = properties.server.sockets
  var faviconDir = properties.server.favicon
  var cookies_secret = properties.server.cookies_secret
  var compression_level = properties.server.compression == null ? 6 : properties.server.compression
  var proxy = properties.server.proxy
  var proxy_strict = properties.server.proxy_strict
  var key_file = properties.ssl && properties.ssl.key
  var crt_file = properties.ssl && properties.ssl.crt
  var mount = properties.faye && properties.faye.mount
  var timeout = properties.faye && properties.faye.timeout
  var engine_type = properties.engine && properties.engine.type || 'jade'
  var file_extantion = properties.engine && properties.engine.file_extantion || '.jade'
  var views_folder = properties.engine && properties.engine.view_folder || 'app/views'
  var static_folder = properties.engine && properties.engine.static_folder
  var admin_users = properties.basic && properties.basic.admin_users
  var realm = properties.basic && properties.basic.realm
  var validator = {customValidators: properties.modules && properties.modules.validator}
  var router = properties.modules && properties.modules.router
  var error = properties.modules && properties.modules.error
  var logger = properties.modules && properties.modules.logger
  // /////////////////////////Building the express server///////////////////////////

  //  Express Server
  var app = express()

  // If proxy procotol is enabled we need to fix standard headers
  if (proxy === 'true') {
    app.all('*', function (req, res, next) {
      req['X-Forwarded-For'] = req.connection.remoteAddress
      req['X-Forwarded-Proto'] = req.connection.proxyPort
      next()
    })
  }

  // Gzip compression middleware
  app.use(compression({level: compression_level}))
  // Adds optional express logging to winston logger
  logger && app.use(expressWinston.logger({
    winstonInstance: logger,
    meta: true,
    colorStatus: true
  }))

  // Adding optional cookies encryption
  if (cookies_secret) {
    app.use(cookieParser(cookies_secret))
    console.log('Your cookies are now encrypted - :)')
  } else {
    console.log('You can\' use Cookies - :(')
  }

  // Dev mode middlewares
  if (environment === 'development') {
    // Allow the client to choose HTTP method as a query param
    app.use(function (req, res, next) {
      req.query.method && (req.method = req.query.method)
      next()
    })
    // Adds optional basic http authentication to all routes
    if (admin_users && realm) {
      var basic = auth.basic({
        realm: realm,
        file: admin_users,
        skipUser: true
      })
      app.use(auth.connect(basic))
    }
  }

  app.use(favicon(faviconDir))                            // Fast fav icon middleware
  app.use(cors())                                         // Allowing CORS comunication
  app.use(bodyParser.json())                              // Support for JSON-encoded bodies
  app.use(bodyParser.urlencoded({extended: true}))        // Support for URL-encoded bodies
  app.engine(file_extantion, consolidate[engine_type])    // assign the template engine to the file extantion
  app.set('view engine', file_extantion)                  // set file extantion as the default extension
  app.set('views', views_folder)                          // Changing default view folder
  app.use(expressValidator(validator))                    // Parameter validation middleware
  router && app.use(router)                               // Load the router to the server
  static_folder && app.use(express.static(static_folder)) // Optional HTTP static server middleware
  logger && app.use(expressWinston.errorLogger({          // Adds optional express error logging to winston logger
    winstonInstance: logger,
    level: 'silly',
    statusLevels: true
  }))

  // HTTP status 404 response middlware
  app.use(function (req, res, next) {
    res.status(404)
    res.format({
      html: function () { res.render('404') },              // respond with html page
      json: function () { res.send({error: 'Not found'}) }, // respond with json
      default: function () { res.send('Not found') }        // default to plain-text
    })
  })
  error && app.use(error)

  // ////////////////////Building The returned Server Object//////////////////////
  var server = {
    port: http_port,
    ssl: false,
    pub_sub: false,
    sockets: false
  }
  // Default http server to wrap the express app in
  var http_server = http

  // Adding optional SSL to the server
  if (key_file && crt_file) {
    var ssl_key = fs.readFileSync(key_file)
    var ssl_cert = fs.readFileSync(crt_file)

    if (ssl_key && ssl_cert) {
      // SSL settings
      var options = {
        key: ssl_key,
        cert: ssl_cert,
        ciphers: 'HIGH:!DSS:!aNULL@STRENGTH',
        honorCipherOrder: true
      }
      // Change http_server to be an HTTPS server
      http_server = https
      // Change SSL flag to true
      server.ssl = true
      // Set port to https port
      server.port = https_port
      console.log('You now have a https server with SSL enabled')
      // set up plain http server
      var https_redirect = express()
      // set up a route to redirect http to https
      https_redirect.use(function (req, res) {
        res.redirect('https://' + req.get('Host') + req.url)
      })
      // have it listen on http to redirect users to https
      https_redirect.listen(http_port)
    } else {
      console.log('Can\'t find SSL certifcate and key files so no SSL for you')
    }
  } else {
    console.log('Can\'t find SSL properties so no SSL for you')
  }

  // If proxy protocol in enabled, we will wrap the http_server we have with proxy server
  if (proxy === 'true') {
    http_server = proxiedHttp.proxy(http_server, {strict: proxy_strict})
  }

  // Create the server using whatever http_server we have now
  if (options) server.http_server = http_server.createServer(options, app)
  else server.http_server = http_server.createServer(app)

  // Adding optional Pub/Sub capabilities using Faye
  if (mount && timeout) {
    server.pub_sub = true
    var bayeux = new faye.NodeAdapter({mount: mount, timeout: timeout})
    //  Faye Server
    bayeux.attach(server)
    console.log('faye started at mount: ' + mount + ', with timeout: ' + timeout)
  } else {
    console.log('Can\'t find faye properties so no pub/sub for you')
  }

  // Adding optional websockers capabilities using Socket.IO
  if (sockets === 'true') {
    // Sockets Server
    server.io_server = io(server.http_server)
    server.io_server.on('connection', function (socket) {
      server.sockets = true
      console.log('user conneced to scoket with id: ' + socket.id)
      socket.on('disconnect', function () {
        console.log('user disconnect from socket')
      })
    })
  }
  return (server)
}
<|endoftext|>