<commit_before>(function() {
    'use strict';

    var fs              = require('fs'),
        zlib            = require('zlib'),
        url             = require('url'),
        querystring     = require('querystring'),

        DIR_JSON        = __dirname + '/../../json/',

        Util            = require('util-io'),
        pipe            = require('pipe-io'),
        ext             = require(DIR_JSON + 'ext'),

    OK                  = 200,
    RANGE               = 206,
    MOVED_PERMANENTLY   = 301,
    FILE_NOT_FOUND      = 404;

    exports.redirect    = redirect;

    exports.send        = send;
    exports.sendError   = sendError;
    exports.sendFile    = sendFile;

    exports.isGZIP      = isGZIP;

    exports.getPathName = getPathName;
    exports.getQuery    = getQuery;

    exports.setHeader   = setHeader;

    /* Функция высылает ответ серверу
     * @param data
     * @param params
     * @param notLog
     */
    function send(data, params, notLog) {
        var p, isGzip, head,
            ret = checkParams(params);

        if (ret) {
            p           = params;
            data        = data;
            isGzip      = p.gzip && isGZIP(p.request);

            head        = generateHeaders({
                name    : p.name,
                cache   : p.cache,
                gzip    : isGzip,
                query   : p.query
            });

            fillHeader(head, p.response);

            if (!notLog)
                Util.log(data);

            /* если браузер поддерживает gzip-сжатие - сжимаем данные*/
            Util.exec.if(!isGzip,
                function() {
                    if (!p.data)
                        p.data      = data;

                    p.response.statusCode = p.status || OK;
                    p.response.end(p.data);
                },

                function(callback) {
                    zlib.gzip (data, function(error, data) {
                        if (!error)
                            p.data = data;
                        else {
                            p.status    = FILE_NOT_FOUND;
                            p.data      = error.message;
                        }

                        callback();
                    });
                });
        }
    }

    /**
     * Функция создаёт заголовки файлов
     * в зависимости от расширения файла
     * перед отправкой их клиенту
     * @param pParams
     *  name - имя файла
     * gzip - данные сжаты gzip'ом
     * query
     * https://developers.google.com/speed/docs/best-practices/caching?hl=ru#LeverageProxyCaching
     */
    function generateHeaders(params) {
        var header, p, extension, type, encoding, isContain, cmp,
            maxAge          = 31337 * 21;

        if (params.name) {
            p               = params,
            extension       = Util.getExt(p.name),
            type            = ext[extension] || 'text/plain',
            encoding        = '';

            /* if type of file any, but img - then we shoud specify charset */
            isContain       = Util.isContainStr(type, ['img', 'image', 'audio']);
            if (!isContain)
                encoding    = '; charset=UTF-8';

            isContain        = Util.isContainStr(p.query, 'download');
            if (isContain)
                type        = 'application/octet-stream';

            header          = {
                'Access-Control-Allow-Origin'   : '*',
                'Content-Type'                  : type  + encoding,
                'Vary'                          : 'Accept-Encoding',
                'Accept-Ranges'                 : 'bytes'
            };

            if (p.time)
                Util.copyObj(header, {
                    'Last-Modified' : p.time
                });

            if (p.range)
                Util.copyObj(header, {
                    'Content-Range' :   'bytes '    + p.range.start +
                                        '-'         + p.range.end   +
                                        '/'         + p.range.sizeTotal,

                    'Content-Length':   p.range.size
                });

            cmp             = Util.strCmp(extension, '.appcache');
            if (!p.cache || cmp)
                maxAge  = 0;

            header['Cache-Control']     = 'max-age=' + maxAge;

            if (p.gzip)
                header['Content-Encoding']  = 'gzip';
        }

        return header;
    }

    function setHeader(pParams) {
        var p, header, gzip,
            lRet = checkParams(pParams);

        if (lRet) {
            p       = pParams;
            gzip    = p.isGzip || isGZIP(p.request) && p.gzip;

            header  = generateHeaders({
                name    : p.name,
                time    : p.time,
                range   : p.range,
                length  : p.length,
                cache   : p.cache,
                gzip    : gzip,
                query   : getQuery(p.request)
            });

            fillHeader(header, p.response);
            p.response.statusCode = p.status || OK;
        }
    }

     function fillHeader(header, response) {
        var isObject    = Util.isObject(header),
            isSent      = response.headersSent;

        if (!isSent && isObject)
            Object.keys(header).forEach(function(name) {
                response.setHeader(name, header[name]);
            });
    }

    /**
     * send file to client thru pipe
     * and gzip it if client support
     *
     */
    function sendFile(params) {
        var p       = params,
            ret     = checkParams(params);

        if (ret)
            fs.lstat(p.name, function(error, stat) {
                var time, length, range, isGzip,
                    options = {};

                if (error) {
                    sendError(error, params);
                } else {
                    isGzip  = isGZIP(p.request) && p.gzip;
                    time    = stat.mtime,
                    length  = stat.size,
                    range   = getRange(p.request, length);

                    if (range)
                        Util.copyObj(p, {
                            range       : range,
                            status      : RANGE
                        });

                    Util.copyObj(p, {
                        time        : time
                    });

                    setHeader(params);

                    options = {
                        gzip    : isGzip,
                        range   : range
                    };

                    pipe.create(p.name, p.response, options, function(error) {
                        if (error)
                            sendError(error, params);
                    });
                }
            });
    }

    /**
     * send error response
     */
    function sendError(error, params) {
        var data, ret       = checkParams(params);

        if (ret) {
            params.status   = FILE_NOT_FOUND;
            data            = error.message || '' + error;

            send(data, params);
        }
    }

    function checkParams(params) {
        var ret = true;

        Util.checkArgs(arguments, ['params']);
        Util.checkArgs([params.name, params.request, params.response], ['name', 'requst', 'response']);

        return ret;
    }

    function getQuery(req) {
        var query, parsed;

        Util.checkArgs(arguments, ['req']);

        parsed  = url.parse(req.url);
        query   = parsed.query;

        return query;
    }

    function getPathName(req) {
        var pathname, parsed;

        Util.checkArgs(arguments, ['req']);

        parsed      = url.parse(req.url);
        pathname    = parsed.pathname;
        /* supporting of Russian language in directory names */
        pathname    = querystring.unescape(pathname);

        return pathname;
    }

    function getRange(req, sizeTotal) {
        var range, start, end, size, parts,
            rangeStr = req.headers.range;

        if (rangeStr) {
            parts   = rangeStr.replace(/bytes=/, '').split('-');
            start   = parts[0];
            end     = parts[1] || sizeTotal - 1;
            size    = (end - start) + 1;

            range   = {
                start       : start - 0,
                end         : end - 0,
                size        : size,
                sizeTotal   : sizeTotal
            };
        }

        return range;
    }

    function isGZIP(req) {
        var enc, is;

        if (req) {
            enc = req.headers['accept-encoding'] || '';
            is  = enc.match(/\bgzip\b/);
        }

        return is;
    }

    /**
     * redirect to another URL
     */
    function redirect(url, response) {
        var header  = {
            'Location': url
        };

        Util.checkArgs(arguments, ['url', 'response']);

        fillHeader(header, response);
        response.statusCode = MOVED_PERMANENTLY;
        response.end();
    }

})();
<commit_msg>fix(ponse) DIR_JSON<commit_after>(function() {
    'use strict';

    var fs              = require('fs'),
        zlib            = require('zlib'),
        url             = require('url'),
        querystring     = require('querystring'),

        DIR_JSON        = __dirname + '/../json/',

        Util            = require('util-io'),
        pipe            = require('pipe-io'),
        ext             = require(DIR_JSON + 'ext'),

    OK                  = 200,
    RANGE               = 206,
    MOVED_PERMANENTLY   = 301,
    FILE_NOT_FOUND      = 404;

    exports.redirect    = redirect;

    exports.send        = send;
    exports.sendError   = sendError;
    exports.sendFile    = sendFile;

    exports.isGZIP      = isGZIP;

    exports.getPathName = getPathName;
    exports.getQuery    = getQuery;

    exports.setHeader   = setHeader;

    /* Функция высылает ответ серверу
     * @param data
     * @param params
     * @param notLog
     */
    function send(data, params, notLog) {
        var p, isGzip, head,
            ret = checkParams(params);

        if (ret) {
            p           = params;
            data        = data;
            isGzip      = p.gzip && isGZIP(p.request);

            head        = generateHeaders({
                name    : p.name,
                cache   : p.cache,
                gzip    : isGzip,
                query   : p.query
            });

            fillHeader(head, p.response);

            if (!notLog)
                Util.log(data);

            /* если браузер поддерживает gzip-сжатие - сжимаем данные*/
            Util.exec.if(!isGzip,
                function() {
                    if (!p.data)
                        p.data      = data;

                    p.response.statusCode = p.status || OK;
                    p.response.end(p.data);
                },

                function(callback) {
                    zlib.gzip (data, function(error, data) {
                        if (!error)
                            p.data = data;
                        else {
                            p.status    = FILE_NOT_FOUND;
                            p.data      = error.message;
                        }

                        callback();
                    });
                });
        }
    }

    /**
     * Функция создаёт заголовки файлов
     * в зависимости от расширения файла
     * перед отправкой их клиенту
     * @param pParams
     *  name - имя файла
     * gzip - данные сжаты gzip'ом
     * query
     * https://developers.google.com/speed/docs/best-practices/caching?hl=ru#LeverageProxyCaching
     */
    function generateHeaders(params) {
        var header, p, extension, type, encoding, isContain, cmp,
            maxAge          = 31337 * 21;

        if (params.name) {
            p               = params,
            extension       = Util.getExt(p.name),
            type            = ext[extension] || 'text/plain',
            encoding        = '';

            /* if type of file any, but img - then we shoud specify charset */
            isContain       = Util.isContainStr(type, ['img', 'image', 'audio']);
            if (!isContain)
                encoding    = '; charset=UTF-8';

            isContain        = Util.isContainStr(p.query, 'download');
            if (isContain)
                type        = 'application/octet-stream';

            header          = {
                'Access-Control-Allow-Origin'   : '*',
                'Content-Type'                  : type  + encoding,
                'Vary'                          : 'Accept-Encoding',
                'Accept-Ranges'                 : 'bytes'
            };

            if (p.time)
                Util.copyObj(header, {
                    'Last-Modified' : p.time
                });

            if (p.range)
                Util.copyObj(header, {
                    'Content-Range' :   'bytes '    + p.range.start +
                                        '-'         + p.range.end   +
                                        '/'         + p.range.sizeTotal,

                    'Content-Length':   p.range.size
                });

            cmp             = Util.strCmp(extension, '.appcache');
            if (!p.cache || cmp)
                maxAge  = 0;

            header['Cache-Control']     = 'max-age=' + maxAge;

            if (p.gzip)
                header['Content-Encoding']  = 'gzip';
        }

        return header;
    }

    function setHeader(pParams) {
        var p, header, gzip,
            lRet = checkParams(pParams);

        if (lRet) {
            p       = pParams;
            gzip    = p.isGzip || isGZIP(p.request) && p.gzip;

            header  = generateHeaders({
                name    : p.name,
                time    : p.time,
                range   : p.range,
                length  : p.length,
                cache   : p.cache,
                gzip    : gzip,
                query   : getQuery(p.request)
            });

            fillHeader(header, p.response);
            p.response.statusCode = p.status || OK;
        }
    }

     function fillHeader(header, response) {
        var isObject    = Util.isObject(header),
            isSent      = response.headersSent;

        if (!isSent && isObject)
            Object.keys(header).forEach(function(name) {
                response.setHeader(name, header[name]);
            });
    }

    /**
     * send file to client thru pipe
     * and gzip it if client support
     *
     */
    function sendFile(params) {
        var p       = params,
            ret     = checkParams(params);

        if (ret)
            fs.lstat(p.name, function(error, stat) {
                var time, length, range, isGzip,
                    options = {};

                if (error) {
                    sendError(error, params);
                } else {
                    isGzip  = isGZIP(p.request) && p.gzip;
                    time    = stat.mtime,
                    length  = stat.size,
                    range   = getRange(p.request, length);

                    if (range)
                        Util.copyObj(p, {
                            range       : range,
                            status      : RANGE
                        });

                    Util.copyObj(p, {
                        time        : time
                    });

                    setHeader(params);

                    options = {
                        gzip    : isGzip,
                        range   : range
                    };

                    pipe.create(p.name, p.response, options, function(error) {
                        if (error)
                            sendError(error, params);
                    });
                }
            });
    }

    /**
     * send error response
     */
    function sendError(error, params) {
        var data, ret       = checkParams(params);

        if (ret) {
            params.status   = FILE_NOT_FOUND;
            data            = error.message || '' + error;

            send(data, params);
        }
    }

    function checkParams(params) {
        var ret = true;

        Util.checkArgs(arguments, ['params']);
        Util.checkArgs([params.name, params.request, params.response], ['name', 'requst', 'response']);

        return ret;
    }

    function getQuery(req) {
        var query, parsed;

        Util.checkArgs(arguments, ['req']);

        parsed  = url.parse(req.url);
        query   = parsed.query;

        return query;
    }

    function getPathName(req) {
        var pathname, parsed;

        Util.checkArgs(arguments, ['req']);

        parsed      = url.parse(req.url);
        pathname    = parsed.pathname;
        /* supporting of Russian language in directory names */
        pathname    = querystring.unescape(pathname);

        return pathname;
    }

    function getRange(req, sizeTotal) {
        var range, start, end, size, parts,
            rangeStr = req.headers.range;

        if (rangeStr) {
            parts   = rangeStr.replace(/bytes=/, '').split('-');
            start   = parts[0];
            end     = parts[1] || sizeTotal - 1;
            size    = (end - start) + 1;

            range   = {
                start       : start - 0,
                end         : end - 0,
                size        : size,
                sizeTotal   : sizeTotal
            };
        }

        return range;
    }

    function isGZIP(req) {
        var enc, is;

        if (req) {
            enc = req.headers['accept-encoding'] || '';
            is  = enc.match(/\bgzip\b/);
        }

        return is;
    }

    /**
     * redirect to another URL
     */
    function redirect(url, response) {
        var header  = {
            'Location': url
        };

        Util.checkArgs(arguments, ['url', 'response']);

        fillHeader(header, response);
        response.statusCode = MOVED_PERMANENTLY;
        response.end();
    }

})();
<|endoftext|>


<commit_before>// @flow

import moment from 'moment'

export function formatTimeForConversationList(time: number, nowOverride?: number): string {
  const m = moment(time)
  const now = nowOverride ? moment(nowOverride) : moment()
  const today = now.clone().startOf('day')
  const weekOld = today.clone().subtract(7, 'days')

  if (m.isSame(today, 'd')) {
    return m.format('h:mm A')
  } else if (m.isAfter(weekOld)) {
    return m.format('ddd')
  }

  return m.format('MMM D')
}

export function formatTimeForMessages(time: number, nowOverride?: number): string {
  const m = moment(time)
  const now = nowOverride ? moment(nowOverride) : moment()

  if (now.diff(m, 'months') > 6) {
    return m.format('MMM DD YYYY h:mm A') // Jan 5 2016 4:34 PM
  } else if (now.diff(m, 'days') > 6) {
    return m.format('MMM DD h:mm A') // Jan 5 4:34 PM
  } else if (now.diff(m, 'hours') > 22) {
    return m.format('ddd h:mm A') // Wed 4:34 PM
  } else {
    return m.format('h:mm A') // 4:34 PM
  }
}

export function formatTimeForPopup(time: number): string {
  const m = moment(time)
  return m.format('ddd MMM DD h:mm A') // Wed Jan 5 2016 4:34 PM
}

export function formatTimeForRevoked(time: number): string {
  const m = moment(time)
  return m.format('ddd MMM DD') // Wed Jan 5 2016
}
<commit_msg>Clarify timestamps for messages sent yesterday<commit_after>// @flow

import moment from 'moment'

export function formatTimeForConversationList(time: number, nowOverride?: number): string {
  const m = moment(time)
  const now = nowOverride ? moment(nowOverride) : moment()
  const today = now.clone().startOf('day')
  const weekOld = today.clone().subtract(7, 'days')

  if (m.isSame(today, 'd')) {
    return m.format('h:mm A')
  } else if (m.isAfter(weekOld)) {
    return m.format('ddd')
  }

  return m.format('MMM D')
}

export function formatTimeForMessages(time: number, nowOverride?: number): string {
  const m = moment(time)
  const now = nowOverride ? moment(nowOverride) : moment()

  if (!now.isSame(m, 'year')) {
    return m.format('MMM DD YYYY h:mm A') // Jan 5 2016 4:34 PM
  } else if (now.diff(m, 'days') > 6) {
    return m.format('MMM DD h:mm A') // Jan 5 4:34 PM
  } else if (now.diff(m, 'hours') > 48) {
    return m.format('ddd h:mm A') // Wed 4:34 PM
  } else if (!now.isSame(m, 'day')) {
    return 'Yesterday ' + m.format('h:mm A') // Yesterday 4:34 PM
  } else {
    return m.format('h:mm A') // 4:34 PM
  }
}

export function formatTimeForPopup(time: number): string {
  const m = moment(time)
  return m.format('ddd MMM DD h:mm A') // Wed Jan 5 2016 4:34 PM
}

export function formatTimeForRevoked(time: number): string {
  const m = moment(time)
  return m.format('ddd MMM DD') // Wed Jan 5 2016
}
<|endoftext|>


<commit_before>/*!
 * Connect - HTTPServer
 * Copyright(c) 2010 Sencha Inc.
 * Copyright(c) 2011 TJ Holowaychuk
 * MIT Licensed
 */

/**
 * Module dependencies.
 */

var escapeHtml = require('escape-html');
var http = require('http');
var debug = require('debug')('connect:dispatcher');
var parseUrl = require('parseurl');

// prototype

var app = module.exports = {};

// environment

var env = process.env.NODE_ENV || 'development';

/**
 * Utilize the given middleware `handle` to the given `route`,
 * defaulting to _/_. This "route" is the mount-point for the
 * middleware, when given a value other than _/_ the middleware
 * is only effective when that segment is present in the request's
 * pathname.
 *
 * For example if we were to mount a function at _/admin_, it would
 * be invoked on _/admin_, and _/admin/settings_, however it would
 * not be invoked for _/_, or _/posts_.
 *
 * Examples:
 *
 *      var app = connect();
 *      app.use(connect.favicon());
 *      app.use(connect.logger());
 *      app.use(connect.static(__dirname + '/public'));
 *
 * If we wanted to prefix static files with _/public_, we could
 * "mount" the `static()` middleware:
 *
 *      app.use('/public', connect.static(__dirname + '/public'));
 *
 * This api is chainable, so the following is valid:
 *
 *      connect()
 *        .use(connect.favicon())
 *        .use(connect.logger())
 *        .use(connect.static(__dirname + '/public'))
 *        .listen(3000);
 *
 * @param {String|Function|Server} route, callback or server
 * @param {Function|Server} callback or server
 * @return {Server} for chaining
 * @api public
 */

app.use = function(route, fn){
  // default route to '/'
  if ('string' != typeof route) {
    fn = route;
    route = '/';
  }

  // wrap sub-apps
  if ('function' == typeof fn.handle) {
    var server = fn;
    server.route = route;
    fn = function(req, res, next){
      server.handle(req, res, next);
    };
  }

  // wrap vanilla http.Servers
  if (fn instanceof http.Server) {
    fn = fn.listeners('request')[0];
  }

  // strip trailing slash
  if ('/' == route[route.length - 1]) {
    route = route.slice(0, -1);
  }

  // add the middleware
  debug('use %s %s', route || '/', fn.name || 'anonymous');
  this.stack.push({ route: route, handle: fn });

  return this;
};

/**
 * Handle server requests, punting them down
 * the middleware stack.
 *
 * @api private
 */

app.handle = function(req, res, out) {
  var stack = this.stack
    , search = 1 + req.url.indexOf('?')
    , pathlength = search ? search - 1 : req.url.length
    , fqdn = 1 + req.url.substr(0, pathlength).indexOf('://')
    , protohost = fqdn ? req.url.substr(0, req.url.indexOf('/', 2 + fqdn)) : ''
    , removed = ''
    , slashAdded = false
    , index = 0;

  function next(err) {
    var layer, path, c;

    if (slashAdded) {
      req.url = req.url.substr(1);
      slashAdded = false;
    }

    req.url = protohost + removed + req.url.substr(protohost.length);
    req.originalUrl = req.originalUrl || req.url;
    removed = '';

    // next callback
    layer = stack[index++];

    // all done
    if (!layer) {
      // delegate to parent
      if (out) return out(err);

      // unhandled error
      if (err) {
        // default to 500
        if (res.statusCode < 400) res.statusCode = 500;
        debug('default %s', res.statusCode);

        // respect err.status
        if (err.status) res.statusCode = err.status;

        // production gets a basic error message
        var msg = 'production' == env
          ? http.STATUS_CODES[res.statusCode]
          : err.stack || err.toString();
        msg = escapeHtml(msg).replace(/\n/g, '
').replace(/  /g, '  ');

        // log to stderr in a non-test env
        if ('test' != env) console.error(err.stack || err.toString());
        if (res.headersSent) return req.socket.destroy();
        res.setHeader('Content-Type', 'text/html');
        res.setHeader('Content-Length', Buffer.byteLength(msg));
        if ('HEAD' == req.method) return res.end();
        res.end(msg + '\n');
      } else if (!res.headersSent) {
        debug('default 404');
        res.statusCode = 404;
        res.setHeader('Content-Type', 'text/html');
        if ('HEAD' == req.method) return res.end();
        res.end('Cannot ' + escapeHtml(req.method) + ' ' + escapeHtml(req.originalUrl) + '\n');
      }
      return;
    }

    try {
      path = parseUrl(req).pathname;
      if (undefined == path) path = '/';

      // skip this layer if the route doesn't match.
      if (0 != path.toLowerCase().indexOf(layer.route.toLowerCase())) return next(err);

      c = path[layer.route.length];
      if (c && '/' != c && '.' != c) return next(err);

      // Call the layer handler
      // Trim off the part of the url that matches the route
      removed = layer.route;
      req.url = protohost + req.url.substr(protohost.length + removed.length);

      // Ensure leading slash
      if (!fqdn && '/' != req.url[0]) {
        req.url = '/' + req.url;
        slashAdded = true;
      }

      debug('%s %s : %s', layer.handle.name || 'anonymous', layer.route, req.originalUrl);
      var arity = layer.handle.length;
      if (err) {
        if (arity === 4) {
          layer.handle(err, req, res, next);
        } else {
          next(err);
        }
      } else if (arity < 4) {
        layer.handle(req, res, next);
      } else {
        next();
      }
    } catch (e) {
      next(e);
    }
  }
  next();
};

/**
 * Listen for connections.
 *
 * This method takes the same arguments
 * as node's `http.Server#listen()`.
 *
 * HTTP and HTTPS:
 *
 * If you run your application both as HTTP
 * and HTTPS you may wrap them individually,
 * since your Connect "server" is really just
 * a JavaScript `Function`.
 *
 *      var connect = require('connect')
 *        , http = require('http')
 *        , https = require('https');
 *
 *      var app = connect();
 *
 *      http.createServer(app).listen(80);
 *      https.createServer(options, app).listen(443);
 *
 * @return {http.Server}
 * @api public
 */

app.listen = function(){
  var server = http.createServer(this);
  return server.listen.apply(server, arguments);
};
<commit_msg>build: remove broken example from source code<commit_after>/*!
 * Connect - HTTPServer
 * Copyright(c) 2010 Sencha Inc.
 * Copyright(c) 2011 TJ Holowaychuk
 * MIT Licensed
 */

/**
 * Module dependencies.
 */

var escapeHtml = require('escape-html');
var http = require('http');
var debug = require('debug')('connect:dispatcher');
var parseUrl = require('parseurl');

// prototype

var app = module.exports = {};

// environment

var env = process.env.NODE_ENV || 'development';

/**
 * Utilize the given middleware `handle` to the given `route`,
 * defaulting to _/_. This "route" is the mount-point for the
 * middleware, when given a value other than _/_ the middleware
 * is only effective when that segment is present in the request's
 * pathname.
 *
 * For example if we were to mount a function at _/admin_, it would
 * be invoked on _/admin_, and _/admin/settings_, however it would
 * not be invoked for _/_, or _/posts_.
 *
 * @param {String|Function|Server} route, callback or server
 * @param {Function|Server} callback or server
 * @return {Server} for chaining
 * @api public
 */

app.use = function(route, fn){
  // default route to '/'
  if ('string' != typeof route) {
    fn = route;
    route = '/';
  }

  // wrap sub-apps
  if ('function' == typeof fn.handle) {
    var server = fn;
    server.route = route;
    fn = function(req, res, next){
      server.handle(req, res, next);
    };
  }

  // wrap vanilla http.Servers
  if (fn instanceof http.Server) {
    fn = fn.listeners('request')[0];
  }

  // strip trailing slash
  if ('/' == route[route.length - 1]) {
    route = route.slice(0, -1);
  }

  // add the middleware
  debug('use %s %s', route || '/', fn.name || 'anonymous');
  this.stack.push({ route: route, handle: fn });

  return this;
};

/**
 * Handle server requests, punting them down
 * the middleware stack.
 *
 * @api private
 */

app.handle = function(req, res, out) {
  var stack = this.stack
    , search = 1 + req.url.indexOf('?')
    , pathlength = search ? search - 1 : req.url.length
    , fqdn = 1 + req.url.substr(0, pathlength).indexOf('://')
    , protohost = fqdn ? req.url.substr(0, req.url.indexOf('/', 2 + fqdn)) : ''
    , removed = ''
    , slashAdded = false
    , index = 0;

  function next(err) {
    var layer, path, c;

    if (slashAdded) {
      req.url = req.url.substr(1);
      slashAdded = false;
    }

    req.url = protohost + removed + req.url.substr(protohost.length);
    req.originalUrl = req.originalUrl || req.url;
    removed = '';

    // next callback
    layer = stack[index++];

    // all done
    if (!layer) {
      // delegate to parent
      if (out) return out(err);

      // unhandled error
      if (err) {
        // default to 500
        if (res.statusCode < 400) res.statusCode = 500;
        debug('default %s', res.statusCode);

        // respect err.status
        if (err.status) res.statusCode = err.status;

        // production gets a basic error message
        var msg = 'production' == env
          ? http.STATUS_CODES[res.statusCode]
          : err.stack || err.toString();
        msg = escapeHtml(msg).replace(/\n/g, '
').replace(/  /g, '  ');

        // log to stderr in a non-test env
        if ('test' != env) console.error(err.stack || err.toString());
        if (res.headersSent) return req.socket.destroy();
        res.setHeader('Content-Type', 'text/html');
        res.setHeader('Content-Length', Buffer.byteLength(msg));
        if ('HEAD' == req.method) return res.end();
        res.end(msg + '\n');
      } else if (!res.headersSent) {
        debug('default 404');
        res.statusCode = 404;
        res.setHeader('Content-Type', 'text/html');
        if ('HEAD' == req.method) return res.end();
        res.end('Cannot ' + escapeHtml(req.method) + ' ' + escapeHtml(req.originalUrl) + '\n');
      }
      return;
    }

    try {
      path = parseUrl(req).pathname;
      if (undefined == path) path = '/';

      // skip this layer if the route doesn't match.
      if (0 != path.toLowerCase().indexOf(layer.route.toLowerCase())) return next(err);

      c = path[layer.route.length];
      if (c && '/' != c && '.' != c) return next(err);

      // Call the layer handler
      // Trim off the part of the url that matches the route
      removed = layer.route;
      req.url = protohost + req.url.substr(protohost.length + removed.length);

      // Ensure leading slash
      if (!fqdn && '/' != req.url[0]) {
        req.url = '/' + req.url;
        slashAdded = true;
      }

      debug('%s %s : %s', layer.handle.name || 'anonymous', layer.route, req.originalUrl);
      var arity = layer.handle.length;
      if (err) {
        if (arity === 4) {
          layer.handle(err, req, res, next);
        } else {
          next(err);
        }
      } else if (arity < 4) {
        layer.handle(req, res, next);
      } else {
        next();
      }
    } catch (e) {
      next(e);
    }
  }
  next();
};

/**
 * Listen for connections.
 *
 * This method takes the same arguments
 * as node's `http.Server#listen()`.
 *
 * HTTP and HTTPS:
 *
 * If you run your application both as HTTP
 * and HTTPS you may wrap them individually,
 * since your Connect "server" is really just
 * a JavaScript `Function`.
 *
 *      var connect = require('connect')
 *        , http = require('http')
 *        , https = require('https');
 *
 *      var app = connect();
 *
 *      http.createServer(app).listen(80);
 *      https.createServer(options, app).listen(443);
 *
 * @return {http.Server}
 * @api public
 */

app.listen = function(){
  var server = http.createServer(this);
  return server.listen.apply(server, arguments);
};
<|endoftext|>


<commit_before>"use strict";

var
  http = require('http'),
  httpProxy = require('http-proxy'),
  validUrl = require('valid-url'),
  url = require('url'),
  _ = require('lodash'),
  redisBackend = require('./redis-backend'),
  bunyan = require('bunyan');

var log = bunyan.createLogger({
    name: 'redbird',
});

function ReverseProxy(backend, opts){
  backend = backend || redisBackend();
  // TODO: If no backend is specified just instantiate a static proxy.

  opts = opts || {};

  opts.port = opts.port || 8080;

  if(!(this instanceof ReverseProxy)){
    return new ReverseProxy(backend, opts);
  }

  //
  // Create a proxy server with custom application logic
  //
  var proxy = httpProxy.createProxyServer();

  //
  // Routing table.
  //
  var routing = this.routing = {};

  var server = require('http').createServer(function(req, res) {
    var target = getTarget(routing, req);
    if(target){
      proxy.web(req, res, { target: target });
    }else {
      notFound(res);
    }
  });

  //
  // Listen to the `upgrade` event and proxy the
  // WebSocket requests as well.
  //
  server.on('upgrade', function (req, socket, head) {
    log.info({headers: req.headers}, "upgrade to websockets");
    var target = getTarget(routing, req);
    if(target){
      proxy.ws(req, socket, head, {target: target});
    }else{
      notFound(socket);
    }
  });

  server.on('error', function(err){
    log.info(err, "Server Error");
  })

  proxy.on('error', function(err){
    log.error(err, "Proxy Error");
  })

  server.listen(opts.port);

  log.info(opts.port, "Started a Redbird reverse proxy server");
}

ReverseProxy.prototype.register = function(src, target){
  register(this.routing, src, target);
}

ReverseProxy.prototype.unregister = function(src, target){
  unregister(this.routing, src, target);
}

//
// We could easily add round-robin to this function.
//
function getTarget(routing, req){
  if(req.headers.host){
    var src = req.headers.host.split(':')[0]
    var host = routing[src];

    if(host){
      log.info(host, "get target")
      var keys = _.keys(host);
      log.info(keys, "keys");

      if(keys.length > 1){
        //
        // We would like to optimize this so that we do
        // not have to sort per request.
        //
        keys = _.sortBy(keys, function(key){
          return -key.length;
        });

        var key = _.find(keys, function(key){
          // remove prefix
          return startsWith(req.url, key);
        });

        if(key){
          if(key.length > 1){
            req.url = req.url.substr(key.length);
            req.url = req.url || '/'
          }
          log.info(host[key] + req.url, "Proxing to")
          return host[key];
        }

      }else{
        return host[keys[0]];
      }
    }
  }
}

function notFound(res){
  res.write("Not Found");
  res.statusCode = 404;
  res.end();
}

function startsWith(input, str){
  return input.indexOf(str) === 0
}

function register(routing, src, target){
  if(!src || !target) return;

  if(validUrl.isUri(target)){
    var parsedUrl = url.parse(target);
    target = parsedUrl.hostname + ':' + parsedUrl.port;
  }

  target = setHttp(target);

  src = setHttp(src);
  src = url.parse(src);

  var host = routing[src.hostname] = routing[src.hostname] || {};
  host[src.pathname || '/'] = target;

  log.info({src: src, target: target}, "Registered a new route");
}

//
// TODO: Implement unregister.
//
function unregister(routing, src, target){

}

//
// https://stackoverflow.com/questions/18052919/javascript-regular-expression-to-add-protocol-to-url-string/18053700#18053700
// Adds http protocol if non specified.
function setHttp(link) {
  if (link.search(/^http[s]?\:\/\//) == -1) {
    link = 'http://' + link;
  }
  return link;
}

module.exports = ReverseProxy;
<commit_msg>added support for targets with pathnames<commit_after>"use strict";

var
  http = require('http'),
  httpProxy = require('http-proxy'),
  validUrl = require('valid-url'),
  parse_url = require('url').parse,
  path = require('path'),
  _ = require('lodash'),
  redisBackend = require('./redis-backend'),
  bunyan = require('bunyan');

var log = bunyan.createLogger({
    name: 'redbird',
});

function ReverseProxy(backend, opts){
  backend = backend || redisBackend();
  // TODO: If no backend is specified just instantiate a static proxy.

  opts = opts || {};

  opts.port = opts.port || 8080;

  if(!(this instanceof ReverseProxy)){
    return new ReverseProxy(backend, opts);
  }

  //
  // Create a proxy server with custom application logic
  //
  var proxy = httpProxy.createProxyServer();

  //
  // Routing table.
  //
  var routing = this.routing = {};

  var server = require('http').createServer(function(req, res) {
    var target = getTarget(routing, req);
    if(target){
      proxy.web(req, res, { target: target });
    }else {
      notFound(res);
    }
  });

  //
  // Listen to the `upgrade` event and proxy the
  // WebSocket requests as well.
  //
  server.on('upgrade', function (req, socket, head) {
    log.info({headers: req.headers}, "upgrade to websockets");
    var target = getTarget(routing, req);
    if(target){
      proxy.ws(req, socket, head, {target: target});
    }else{
      notFound(socket);
    }
  });

  server.on('error', function(err){
    log.info(err, "Server Error");
  })

  proxy.on('error', function(err){
    log.error(err, "Proxy Error");
  })

  server.listen(opts.port);

  log.info(opts.port, "Started a Redbird reverse proxy server");
}

/**
  Register a new route.

  @src {String|URL} A string a url parsed by node url module.
  Note that port is ignored, since the proxy just listens to one port.

  @target {String|URL} A string a url parsed by node url module.
*/
ReverseProxy.prototype.register = function(src, target){
  register(this.routing, src, target);
}

ReverseProxy.prototype.unregister = function(src, target){
  unregister(this.routing, src, target);
}

//
// TODO: add round-robin to this function.
//
function getTarget(routing, req){
  if(req.headers.host){
    var src = req.headers.host.split(':')[0]
    var host = routing[src];
    var target;

    if(host){
      log.info(host, "get target")
      var keys = _.keys(host);
      log.info(keys, "keys");

      if(keys.length > 1){
        //
        // We would like to optimize this so that we do
        // not have to sort per request.
        //
        keys = _.sortBy(keys, function(key){
          return -key.length;
        });

        var key = _.find(keys, function(key){
          // remove prefix
          return startsWith(req.url, key);
        });

        if(key){
          if(key.length > 1){
            req.url = req.url.substr(key.length);
            req.url = req.url || '/'
          }
          target = host[key];
        }
      }else{
        target = host[keys[0]];
      }

      if(target.pathname){
        req.url = path.join(target.pathname, req.url);
      }

      log.info("Proxing to %s", target.host + req.url)
      return target;
    }else{
     log.warn('no valid target found for request %s', src)
    }
  }
}

function notFound(res){
  res.write("Not Found");
  res.statusCode = 404;
  res.end();
}

function startsWith(input, str){
  return input.indexOf(str) === 0
}

function register(routing, src, target){
  if(!src || !target) return;

  src = prepareUrl(src);
  target = prepareUrl(target);

  var host = routing[src.hostname] = routing[src.hostname] || {};
  host[src.pathname || '/'] = target;

  log.info({src: src, target: target}, "Registered a new route");
}

function prepareUrl(url){
  if(_.isString(url)){
    url = setHttp(url);

    if(!validUrl.isHttpUri(url)){
      throw Error('uri is not a valid http uri ' + url);
    }

    url = parse_url(url);
  }
  return url;
}

//
// TODO: Implement unregister.
//
function unregister(routing, src, target){
  if(!src) return;

  src = prepareUrl(src);
  target = target ? prepareUrl(target) : null;

}

//
// https://stackoverflow.com/questions/18052919/javascript-regular-expression-to-add-protocol-to-url-string/18053700#18053700
// Adds http protocol if non specified.
function setHttp(link) {
  if (link.search(/^http[s]?\:\/\//) == -1) {
    link = 'http://' + link;
  }
  return link;
}

module.exports = ReverseProxy;

<|endoftext|>


<commit_before>const rocky = require('rocky')
const Rule = require('./rule')
const rules = require('./rules')
const Poison = require('./poison')
const poisons = require('./poisons')
const Directive = require('./directive')

const responseBody = rocky.middleware.responseBody
responseBody.$name = '$outgoingInterceptor$'

module.exports = rocky.Rocky

var RockyBase = rocky.Base

RockyBase.prototype.rule =
RockyBase.prototype.filter = Directive.prototype.rule

RockyBase.prototype.enable = function (poison, phase) {
  return this._callMethod(this._poisonsStack(phase), 'enable', poison)
}

RockyBase.prototype.enableOutgoing = function (poison) {
  return this.enable(poison, 'outgoing')
}

RockyBase.prototype.disable = function (poison, phase) {
  return this._callMethod(this._poisonsStack(phase), 'disable', poison)
}

RockyBase.prototype.disableOutgoing = function (poison) {
  return this.disable(poison, 'outgoing')
}

RockyBase.prototype.remove = function (poison, phase) {
  var stack = this._poisonsStack(phase || 'incoming')
  return this._remove(stack, poison)
}

RockyBase.prototype.removeOutgoing = function (poison) {
  return this.remove(poison, 'outgoing')
}

RockyBase.prototype.isEnabled = function (poison, phase) {
  var stack = this._poisonsStack(phase || 'incoming')
  return this._callMethod(stack, 'isEnabled', poison)
}

RockyBase.prototype.isEnabledOutgoing = function (poison) {
  return this.isEnabled(poison, 'outgoing')
}

RockyBase.prototype.disableAll =
RockyBase.prototype.disablePoisons = function () {
  return this._disableAll(this._poisonsStack())
}

RockyBase.prototype.getPoison = function (poison, phase) {
  var stack = this._poisonsStack(phase)
  var directive = this._getDirective(stack, poison)
  if (!directive) return null
  if (phase && directive.phase !== phase) return null
  return directive
}

RockyBase.prototype.getOutgoingPoison = function (poison) {
  return this.getPoison(poison, 'outgoing')
}

RockyBase.prototype.getPoisons = function () {
  return this._getAll(this._poisonsStack())
}

RockyBase.prototype.getIncomingPoisons = function () {
  return this._getAll(this._inPoisons)
}

RockyBase.prototype.getOutgoingPoisons = function () {
  return this._getAll(this._outPoisons)
}

RockyBase.prototype.withRule =
RockyBase.prototype.poisonRule =
RockyBase.prototype.poisonFilter = function (rule) {
  if (this.lastPoison) {
    this.lastPoison.rule(rule)
  }
  return this
}

RockyBase.prototype.flush =
RockyBase.prototype.flushPoisons = function () {
  this._inPoisons.stack.splice(0)
  this._outPoisons.stack.splice(0)
  return this
}

RockyBase.prototype.poison =
RockyBase.prototype.usePoison =
RockyBase.prototype.incomingPoison = function (poison) {
  poison = createPoison(poison)
  this._inPoisons(poison.handler())
  this.lastPoison = poison
  return this
}

RockyBase.prototype.outgoingPoison =
RockyBase.prototype.responsePoison = function (poison) {
  poison = createPoison(poison)

  if (!this._outPoisonsEnabled) {
    this._outPoisonsEnabled = true
    this.poison(responseBody(function (req, res, next) {
      this._outPoisons.run(req, res, next)
    }.bind(this)))
  }

  poison.phase = 'outgoing'
  this._outPoisons(poison.handler())
  this.lastPoison = poison

  return this
}

RockyBase.prototype._poisonsStack = function (phase) {
  if (phase) {
    return phase === 'outgoing'
      ? this._outPoisons
      : this._inPoisons
  }

  var inPoisons = this._inPoisons.stack
  var outPoisons = this._outPoisons.stack
  var stack = inPoisons.concat(outPoisons)

  return { stack: stack }
}

/**
 * Extend prototype chain
 */

Object.keys(Rule.prototype).forEach(function (key) {
  RockyBase.prototype[key] = Rule.prototype[key]
})

/**
 * Private helpers
 */

function createPoison(poison) {
  return (poison instanceof Poison)
    ? poison
    : new Poison(poison)
}
<commit_msg>refactor(proxy)<commit_after>const rocky = require('rocky')
const Rule = require('./rule')
const rules = require('./rules')
const Poison = require('./poison')
const poisons = require('./poisons')
const Directive = require('./directive')

module.exports = rocky.Rocky

var RockyBase = rocky.Base

RockyBase.prototype.rule =
RockyBase.prototype.filter = Directive.prototype.rule

RockyBase.prototype.enable = function (poison, phase) {
  return this._callMethod(getPoisonsStack(this, phase), 'enable', poison)
}

RockyBase.prototype.enableOutgoing = function (poison) {
  return this.enable(poison, 'outgoing')
}

RockyBase.prototype.disable = function (poison, phase) {
  return this._callMethod(getPoisonsStack(this, phase), 'disable', poison)
}

RockyBase.prototype.disableOutgoing = function (poison) {
  return this.disable(poison, 'outgoing')
}

RockyBase.prototype.remove = function (poison, phase) {
  var stack = getPoisonsStack(this, phase || 'incoming')
  return this._remove(stack, poison)
}

RockyBase.prototype.removeOutgoing = function (poison) {
  return this.remove(poison, 'outgoing')
}

RockyBase.prototype.isEnabled = function (poison, phase) {
  var stack = getPoisonsStack(this, phase || 'incoming')
  return this._callMethod(stack, 'isEnabled', poison)
}

RockyBase.prototype.isEnabledOutgoing = function (poison) {
  return this.isEnabled(poison, 'outgoing')
}

RockyBase.prototype.disableAll =
RockyBase.prototype.disablePoisons = function () {
  return this._disableAll(getPoisonsStack(this))
}

RockyBase.prototype.getPoison = function (poison, phase) {
  var stack = getPoisonsStack(this, phase)
  var directive = this._getDirective(stack, poison)

  if (!directive || (phase && directive.phase !== phase)) {
    return null
  }

  return directive
}

RockyBase.prototype.getOutgoingPoison = function (poison) {
  return this.getPoison(poison, 'outgoing')
}

RockyBase.prototype.getPoisons = function () {
  return this._getAll(getPoisonsStack(this))
}

RockyBase.prototype.getIncomingPoisons = function () {
  return this._getAll(this._inPoisons)
}

RockyBase.prototype.getOutgoingPoisons = function () {
  return this._getAll(this._outPoisons)
}

RockyBase.prototype.withRule =
RockyBase.prototype.poisonRule =
RockyBase.prototype.poisonFilter = function (rule) {
  if (this.lastPoison) {
    this.lastPoison.rule(rule)
  }
  return this
}

RockyBase.prototype.flush =
RockyBase.prototype.flushPoisons = function () {
  this._inPoisons.stack.splice(0)
  this._outPoisons.stack.splice(0)
  return this
}

RockyBase.prototype.poison =
RockyBase.prototype.usePoison =
RockyBase.prototype.incomingPoison = function (poison) {
  poison = createPoison(poison)
  this._inPoisons(poison.handler())
  this.lastPoison = poison
  return this
}

RockyBase.prototype.outgoingPoison =
RockyBase.prototype.responsePoison = function (poison) {
  poison = createPoison(poison)

  if (!this._outgoingEnabled) {
    outgoingInterceptor(this)
  }

  poison.phase = 'outgoing'
  this._outPoisons(poison.handler())
  this.lastPoison = poison

  return this
}

/**
 * Extend prototype chain
 */

Object.keys(Rule.prototype).forEach(function (key) {
  RockyBase.prototype[key] = Rule.prototype[key]
})

/**
 * Private helpers
 */

function outgoingInterceptor(proxy) {
  var responseBody = rocky.middleware.responseBody
  responseBody.$name = '$outgoingInterceptor$'

  var interceptor = responseBody(function (req, res, next) {
    proxy._outPoisons.run(req, res, next)
  })

  proxy._outgoingEnabled = true
  proxy.poison(interceptor)
}

function getPoisonsStack(proxy, phase) {
  if (phase) {
    return phase === 'outgoing'
      ? proxy._outPoisons
      : proxy._inPoisons
  }

  var inPoisons = proxy._inPoisons.stack
  var outPoisons = proxy._outPoisons.stack
  var stack = inPoisons.concat(outPoisons)

  return { stack: stack }
}

function createPoison(poison) {
  return (poison instanceof Poison)
    ? poison
    : new Poison(poison)
}
<|endoftext|>


<commit_before>'use strict';

var fs = require('fs') ;
var path = require('path') ;
var _ = require('lodash') ;
var allPossibleTargets = [] ;
var dest = [] ;
var debug ;
var config ;
var pingDown = true ;

function createTargets( config ) {
  allPossibleTargets = _.filter( config.targets, function(t) { return t.enabled === true; }) ;
}

function pingTarget( app, target ) {
  var dest =  'sip:' + target.address + ':' + (target.port || 5060) ;
  if( pingDown ) {
    console.log('not pinging target %s because pinging has been temporarily turned off: ', dest) ;
    return;
  }

  //start a timer - if we don't get a response within a second we'll mark it down
  var timeoutHandler = setTimeout( function() {
    //if we get here we timed out
    if( !target.offline ) {
        target.offline = true ;
        console.error('taking target %s offline because it did not respond to ping: ', dest) ;
    }
  }, 1500 ) ;

  //send the OPTIONS ping
  console.log('OPTIONS %s', dest) ;
  app.request({
      uri: dest,
      method: 'OPTIONS',
      headers: {
        'User-Agent': 'drachtio simple proxy'
      }
  }, function( err, req ) {
    if( err ) {
      setTimeout( function() { pingTarget( app, target); }, config.optionsPingInterval) ;
      return console.error('Error pinging %s', target) ;
    }

    req.on('response', function(res){

      console.log('%s: %d %s', dest, res.status, res.reason) ;
      clearTimeout( timeoutHandler ) ;
      if( 200 === res.status ) {
        if( target.offline ) {
           target.offline = false ;
           console.log('bringing target back online: %s', dest) ;
        }
      }
      setTimeout( function() { pingTarget( app, target);}, config.optionsPingInterval) ;
    }) ;
  }) ;
}

function stopPinging() {
  console.log('stopping OPTIONS pings..') ;
  pingDown = true ;
}

function startPinging( app, config ) {
  console.log('start OPTIONS pings to targets at %d ms interval: ', config.optionsPingInterval, JSON.stringify(allPossibleTargets)) ;
  pingDown = false ;
  allPossibleTargets.forEach( function(t) {pingTarget(app,t); }) ;
}

module.exports = function(app) {
  debug = app.debug ;
  config = app.config ;

  //min allowed ping interval is 2 seconds
  config.optionsPingInterval = Math.max(config.optionsPingInterval || 5000, 2000) ;
  console.log('options ping interval will be %d milliseconds', config.optionsPingInterval) ;

  createTargets(config) ;
  startPinging(app, config) ;

  //watch config file for changes - we allow the user to dynamically add or remove targets
  var configPath = path.resolve(__dirname) + '/../config.js' ;
  fs.watchFile(configPath, function () {
    try {
      console.log('config.js was just modified...') ;
      stopPinging() ;

      delete require.cache[require.resolve(configPath)] ;
      config = require(configPath) ;
      createTargets( config ) ;

      console.log('proxy targets are now: ',
        _.map( allPossibleTargets, function(t) { return _.pick(t, ['address','port','enabled','optionsPing']);}));

      startPinging(app, config) ;

    } catch( err ) {
      console.error('Error re-reading config.js after modification; check to ensure there are no syntax errors: ', err) ;
      startPinging(app, config) ;
    }
  }) ;

  app.invite( function(req,res) {

    //filter out offline servers
    var onlineServers = _.filter( allPossibleTargets, function(t) { return !t.offline; }) ;

    if( 0 === onlineServers.length ) {
      console.error('there are no servers online currently') ;
      return res.send(480) ;
    }

    var dest = _.map(  onlineServers, function(t) { return t.address + ':' + (t.port || 5060); }) ;

    debug('proxy destinations are: ', dest) ;

    //finally....here's the magic: proxy the request, attempting destinations in order until we connect or all fail
    req.proxy({
      remainInDialog: false,
      destination: dest
    }, function(err, results) {
      if( err ) console.error('Error proxying: ', err) ;

      //this is voluminous, but if you want to know what happened all the detail is here..
      debug('proxy result: ', JSON.stringify(results)) ;
    }) ;

    //round-robin the next starting server in the list
    var item = allPossibleTargets.shift() ;
    allPossibleTargets.push( item ) ;
  }) ;
} ;

<commit_msg>stop sending OPTIONs ping to a server when it is removed from the config file<commit_after>'use strict';

var fs = require('fs') ;
var path = require('path') ;
var _ = require('lodash') ;
var allPossibleTargets = [] ;
var dest = [] ;
var debug ;
var config ;
var pingDown = true ;

function createTargets( config ) {
  allPossibleTargets = _.filter( config.targets, function(t) { return t.enabled === true; }) ;
}
function isValidTarget( target ) {
  return _.find( allPossibleTargets, function(t) {return t.address === target.address && t.port === target.port; }) ;
}

function pingTarget( app, target ) {
  var dest =  'sip:' + target.address + ':' + (target.port || 5060) ;
  if( pingDown ) {
    console.log('not pinging target %s because pinging has been temporarily turned off: ', dest) ;
    return;
  }

  //start a timer - if we don't get a response within a second we'll mark it down
  var timeoutHandler = setTimeout( function() {
    //if we get here we timed out
    if( !target.offline ) {
        target.offline = true ;
        console.error('taking target %s offline because it did not respond to ping: ', dest) ;
    }
  }, 1500 ) ;

  //send the OPTIONS ping
  console.log('OPTIONS %s', dest) ;
  app.request({
      uri: dest,
      method: 'OPTIONS',
      headers: {
        'User-Agent': 'drachtio simple proxy'
      }
  }, function( err, req ) {
    if( err ) {
      setTimeout( function() {
        if( isValidTarget( target ) ) {
          pingTarget( app, target);
        }
        else {
          console.log('stop sending OPTIONS ping to %s; it has been removed from the configuration file', dest) ;
        }
      }, config.optionsPingInterval) ;
      return console.error('Error pinging %s', target) ;
    }

    req.on('response', function(res){

      console.log('%s: %d %s', dest, res.status, res.reason) ;
      clearTimeout( timeoutHandler ) ;
      if( 200 === res.status ) {
        if( target.offline ) {
           target.offline = false ;
           console.log('bringing target back online: %s', dest) ;
        }
      }
      setTimeout( function() {
        if( isValidTarget( target ) ) {
          pingTarget( app, target);
        }
        else {
          console.log('stop sending OPTIONS ping to %s; it has been removed from the configuration file', dest) ;
        }
      }, config.optionsPingInterval) ;
    }) ;
  }) ;
}

function stopPinging() {
  console.log('stopping OPTIONS pings..') ;
  pingDown = true ;
}

function startPinging( app, config ) {
  console.log('start OPTIONS pings to targets at %d ms interval: ', config.optionsPingInterval, JSON.stringify(allPossibleTargets)) ;
  pingDown = false ;
  allPossibleTargets.forEach( function(t) {pingTarget(app,t); }) ;
}

module.exports = function(app) {
  debug = app.debug ;
  config = app.config ;

  //min allowed ping interval is 2 seconds
  config.optionsPingInterval = Math.max(config.optionsPingInterval || 5000, 2000) ;
  console.log('options ping interval will be %d milliseconds', config.optionsPingInterval) ;

  createTargets(config) ;
  startPinging(app, config) ;

  //watch config file for changes - we allow the user to dynamically add or remove targets
  var configPath = path.resolve(__dirname) + '/../config.js' ;
  fs.watchFile(configPath, function () {
    try {
      console.log('config.js was just modified...') ;
      stopPinging() ;

      delete require.cache[require.resolve(configPath)] ;
      config = require(configPath) ;
      createTargets( config ) ;

      console.log('proxy targets are now: ',
        _.map( allPossibleTargets, function(t) { return _.pick(t, ['address','port','enabled','optionsPing']);}));

      startPinging(app, config) ;

    } catch( err ) {
      console.error('Error re-reading config.js after modification; check to ensure there are no syntax errors: ', err) ;
      startPinging(app, config) ;
    }
  }) ;

  app.invite( function(req,res) {

    //filter out offline servers
    var onlineServers = _.filter( allPossibleTargets, function(t) { return !t.offline; }) ;

    if( 0 === onlineServers.length ) {
      console.error('there are no servers online currently') ;
      return res.send(480) ;
    }

    var dest = _.map(  onlineServers, function(t) { return t.address + ':' + (t.port || 5060); }) ;

    debug('proxy destinations are: ', dest) ;

    //finally....here's the magic: proxy the request, attempting destinations in order until we connect or all fail
    req.proxy({
      remainInDialog: false,
      destination: dest
    }, function(err, results) {
      if( err ) console.error('Error proxying: ', err) ;

      //this is voluminous, but if you want to know what happened all the detail is here..
      debug('proxy result: ', JSON.stringify(results)) ;
    }) ;

    //round-robin the next starting server in the list
    var item = allPossibleTargets.shift() ;
    allPossibleTargets.push( item ) ;
  }) ;
} ;

<|endoftext|>


<commit_before>var proxies = {};

var _keys= function(obj){

	var keys =[];
	for(var k in obj){
		keys.push(k);
	}
	return keys;
}

var _values = function(obj){

	return _keys(obj).map(function(key){
		return obj[key];
	});
}

var _isSync = function(proc_name,f){

	return _values(proxies[proc_name].sync).indexOf(f) !== -1;
}

/*
 * Returns installed proxies as an array for the procedure name proc_name.
 */
var _list_proxies  = function(proc_name){

	var p = [];
	for(var i=1; i <= size(proc_name); i++ ){

		p.push(	proxies[proc_name].sync[i.toString()] ||
				proxies[proc_name].async[i.toString()] );
	}
	return p;
};

var clear = exports.clear = function(){

	proxies = {};
}

/*
 * Returns the number of installed proxies for the procedure name proc_name.
 */
var size = exports.size  = function(proc_name){

	return _keys(proxies[proc_name].sync).length + _keys(proxies[proc_name].async).length;
};

/*
 * Adds a synchronous proxy to the function proc_name.
 */
exports.add_proxy_sync = function(proc_name, handler){

	if(!proxies[proc_name]){
		proxies[proc_name] = {sync:{},async:{},err:null};
	}

	var nextIndex = size( proc_name ) + 1;
	proxies[proc_name].sync[ nextIndex.toString() ] = handler;
	return this;
};

/*
 * Add an asynchronous proxy to the function proc_name.
 */
exports.add_proxy_async = function(proc_name, handler){

	if(!proxies[proc_name]){
		proxies[proc_name] = {sync:{},async:{},err:null};
	}

	var nextIndex = size(proc_name) + 1;
	proxies[proc_name].async[ nextIndex.toString() ] = handler;
	return this;
};

/*
 * Adds an error handler for async proxies.
 */
exports.add_proxy_err = function(proc_name, handler){

	if(!proxies[proc_name]){
		proxies[proc_name] = {sync:{},async:{},err:null};
	}
	proxies[proc_name].err = handler;
	return this;
}

/*
 * Inject proxies into module.
 */
exports.inject = function(modulee){

	for(var key in proxies){
		(function(){

			var scopekey = key;
			var f = modulee[scopekey];
			var handlers =  _list_proxies(scopekey);
			modulee[scopekey] = function(){

				var args = Array.prototype.slice.call(arguments);
				var loop = function(err){
					if(!err){
						var handler = handlers.shift();
						if(handler){
							if(_isSync(scopekey,handler)){
								handler.apply(this,args);
								loop();
							}else{
								handler.apply(this,args.concat(loop));
							}

						}else{
							f && f.apply(this,args);
						}
					}else if(proxies[scopekey].err){
						proxies[scopekey].err(err);
					}
				};
				loop();
			};
		})();
	}
};
<commit_msg>refactoring + uninject added<commit_after>var proxies = {};

var _check_string = function(str){

	if(typeof str !== "string")
		throw "parameter must be a string";
}

var _keys= function(obj){

	var keys =[];
	for(var k in obj){
		keys.push(k);
	}
	return keys;
}

var _values = function(obj){

	return _keys(obj).map(function(key){
		return obj[key];
	});
}

var _isSync = function(proc_name,f){

	return _values(proxies[proc_name].sync).indexOf(f) !== -1;
}

/*
 * Returns installed proxies as an array for the procedure name proc_name.
 */
var _list_proxies  = function(proc_name){

	var p = [];
	for(var i=1; i <= size(proc_name); i++ ){

		p.push(	proxies[proc_name].sync[i.toString()] ||
				proxies[proc_name].async[i.toString()] );
	}
	return p;
};

var clear = exports.clear = function(){

	proxies = {};
}

/*
 * Returns the number of installed proxies for the procedure name proc_name.
 */
var size = exports.size  = function(proc_name){

	_check_string(proc_name);
	return _keys(proxies[proc_name].sync).length + _keys(proxies[proc_name].async).length;
};

/*
 * Removes proxy functions.
 */
exports.uninject = function(modulee, proc_name){

	proc_name && _check_string(proc_name);

	if(proc_name){
		modulee[proc_name] = proxies[proc_name].func;
		delete proxies[proc_name];
	}else{
		for(var key in proxies){
			modulee[key] = proxies[key].func
			delete proxies[key];
		}
	}

}

/*
 * Adds a synchronous proxy to the function proc_name.
 */
exports.add_proxy_sync = function(proc_name, handler){

	if(!proxies[proc_name]){
		proxies[proc_name] = {sync:{},async:{},err:null,func:null};
	}

	var nextIndex = size( proc_name ) + 1;
	proxies[proc_name].sync[ nextIndex.toString() ] = handler;
	return this;
};

/*
 * Add an asynchronous proxy to the function proc_name.
 */
exports.add_proxy_async = function(proc_name, handler){

	if(!proxies[proc_name]){
		proxies[proc_name] = {sync:{},async:{},err:null,func:null};
	}

	var nextIndex = size(proc_name) + 1;
	proxies[proc_name].async[ nextIndex.toString() ] = handler;
	return this;
};

/*
 * Adds an error handler for async proxies.
 */
exports.add_proxy_err = function(proc_name, handler){

	if(!proxies[proc_name]){
		proxies[proc_name] = {sync:{},async:{},err:null,func:null};
	}
	proxies[proc_name].err = handler;
	return this;
}

/*
 * Inject proxies into module.
 */
exports.inject = function(modulee){

	for(var key in proxies){
		(function(){

			var scopekey = key;
			proxies[scopekey].func = modulee[scopekey];
			var handlers =  _list_proxies(scopekey);
			modulee[scopekey] = function(){

				var args = Array.prototype.slice.call(arguments);
				var loop = function(err){
					if(!err){
						var handler = handlers.shift();
						if(handler){
							if(_isSync(scopekey,handler)){
								handler.apply(this,args);
								loop();
							}else{
								handler.apply(this,args.concat(loop));
							}

						}else{
							proxies[scopekey].func && proxies[scopekey].func.apply(this,args);
						}
					}else if(proxies[scopekey].err){
						proxies[scopekey].err(err);
					}
				};
				loop();
			};
		})();
	}
};
<|endoftext|>


<commit_before>"use strict";

// self.table('tableAlias')
//     .query('primaryKeyValue', 'optionalRangeValue')
//     .fetch()
//     .then(function(){
//     });
// this.query('edit', id)
//     .fetch()
//     .then(d.resolve);

// this.query('edit', id)
//     .remove().then(d.resolve);

// this.put('edit', id, range)
//     .set({})
//     .then(d.resolve);

// this.query('edit', id)
//     .update()
//     .set()
//     .commit().then(d.resolve);

// this.batch()
//     .add('edit');

var UpdateQuery = require('./update-query'),
    log = require('plog')('mambo.query').level('error');

function Query(model, alias, hash, range){
    if(!model.schema(alias).range){
        throw new TypeError('Query only for hash/range tables.  Should call Model.get.');
    }
    this.model = model;
    this.alias = alias;
    this.hash = hash;
    this.range = range;
    this.fieldNames = null;
    this.consistentRead = false;
    this._limit = -1;
    this.scanForward = true;
    this.startKey = null;
    this.filter = {};
    this.useIndex = null;
}

// @todo (lucas) Add where to implement range key conditions.
Query.prototype.start = function(hash, range){
    this.startKey = [hash, range];
    return this;
};

Query.prototype.limit = function(l){
    this._limit = l;
    return this;
};

Query.prototype.consistent = function(){
    this.consistentRead = true;
    return this;
};

Query.prototype.reverse = function(){
    this.scanForward = false;
    return this;
};

Query.prototype.fields = function(fieldNames){
    this.fieldNames = fieldNames;
    return this;
};

Query.prototype.update = function(){
    return new UpdateQuery(this.model, this.alias, this.hash, this.range);
};

// Use a secondary index.
Query.prototype.index = function(name){
    this.useIndex = name;
    return this;
};

Query.prototype.fetch = function(){
    var opts = {},
        schema = this.model.schema(this.alias);

    if(this._limit > -1){
        opts.limit = this._limit;
    }

    if(this.consistentRead){
        opts.consistentRead = this.consistentRead;
    }

    if(!this.scanForward){
        opts.scanIndexForward = this.scanForward;
    }

    if(this.fieldNames){
        opts.attributesToGet =  this.fieldNames;
    }

    if(this.useIndex){
        opts.index = this.useIndex;
    }

    if(this.filter){
        Object.keys(this.filter).forEach(function(key){
            var f = new Filter(schema, key, this.filter[key]);
            opts.rangeKeyCondition = f.export();
        }.bind(this));
    }
    log.silly('Calling query for `' + this.alias + '`');

    return this.model.query(this.alias, this.hash, opts);
};

// module.exports.query('song')
//     // .where('id', 'IN', [1, 2, 3])
//     // .where('loves', '<', 1)
//     // .where('trending', '=', null)
//     // .where('title', '!=', null)
//     // .where('bio', 'NULL')
//     // .where('location', 'NOT_NULL')
//     // .where('started', 'BETWEEN', [1990, 1992])
//     // .fetch()
//     // .then(function(results){
//     // });
Query.prototype.where = function(key, operator, value){
    operator = expandOperator(operator) || operator;
    var f = {};
    f[operator] = value;
    this.filter[key] = f;
    return this;
};

function expandOperator(op){
    switch(op){
        case '>':
            return 'GT';

        case '>=':
            return 'GE';

        case '<':
            return 'LT';

        case '<=':
            return 'LE';

        case '!=':
            return 'NE';

        case '=' || '==' || '===':
            return 'EQ';

        default:
            return undefined;
    }
}

var opToArgLength = {
    'EQ': 1,
    'NE': 1,
    'LE': 1,
    'LT': 1,
    'GE': 1,
    'GT': 1,
    'NOT_NULL': 0,
    'NULL': 0,
    'CONTAINS': 1,
    'NOT_CONTAINS': 1,
    'BEGINS_WITH': 1,
    'IN': null,
    'BETWEEN': 2
};

function Filter(schema, key, data){
    this.schema = schema;
    this.field = schema.field(key);
    this.operator = undefined;
    this.value = [];

    if(data === new Object(data)){
        this.operator = Object.keys(data)[0];
        this.value = data[this.operator];
        if(!Array.isArray(this.value)){
            this.value = [this.value];
        }
    }
    else {
        this.operator = data;
        // @todo (lucas) Assert its actually a valueles condition.
    }
    var expectLen = opToArgLength[this.operator];
    if(expectLen !== null && this.value.length !== expectLen){
        throw new Error('Invalid number of args for ' +
            key + ' filter: ' +this.operator + ' ' + this.value);
    }
}

Filter.prototype.export = function(){
    var ret = {
        'AttributeValueList': [],
        'ComparisonOperator': 'EQ'
    };
    if(this.value){
        ret.AttributeValueList = this.value.map(function(val){
            var ret = {};
            ret[this.field.shortType] = this.field.export(val);
            return ret;
        }.bind(this));
    }
    if(this.operator){
        ret.ComparisonOperator = this.operator;
    }

    return ret;
};

module.exports = Query;<commit_msg>Cleanup<commit_after>"use strict";

// self.table('tableAlias')
//     .query('primaryKeyValue', 'optionalRangeValue')
//     .fetch(done);
//
// this.query('edit', id)
//     .fetch(done);

// this.query('edit', id)
//     .remove(done);

// this.put('edit', id, range)
//     .set({})

// this.query('edit', id)
//     .update()
//     .set()
//     .commit(done);

// this.batch()
//     .add('edit');

var UpdateQuery = require('./update-query'),
    log = require('plog')('mambo.query').level('error');

function Query(model, alias, hash, range){
    if(!model.schema(alias).range){
        throw new TypeError('Query only for hash/range tables.  Should call Model.get.');
    }
    this.model = model;
    this.alias = alias;
    this.hash = hash;
    this.range = range;
    this.fieldNames = null;
    this.consistentRead = false;
    this._limit = -1;
    this.scanForward = true;
    this.startKey = null;
    this.filter = {};
    this.useIndex = null;
}

// @todo (lucas) Add where to implement range key conditions.
Query.prototype.start = function(hash, range){
    this.startKey = [hash, range];
    return this;
};

Query.prototype.limit = function(l){
    this._limit = l;
    return this;
};

Query.prototype.consistent = function(){
    this.consistentRead = true;
    return this;
};

Query.prototype.reverse = function(){
    this.scanForward = false;
    return this;
};

Query.prototype.fields = function(fieldNames){
    this.fieldNames = fieldNames;
    return this;
};

Query.prototype.update = function(){
    return new UpdateQuery(this.model, this.alias, this.hash, this.range);
};

// Use a secondary index.
Query.prototype.index = function(name){
    this.useIndex = name;
    return this;
};

Query.prototype.fetch = function(){
    var opts = {},
        schema = this.model.schema(this.alias);

    if(this._limit > -1){
        opts.limit = this._limit;
    }

    if(this.consistentRead){
        opts.consistentRead = this.consistentRead;
    }

    if(!this.scanForward){
        opts.scanIndexForward = this.scanForward;
    }

    if(this.fieldNames){
        opts.attributesToGet =  this.fieldNames;
    }

    if(this.useIndex){
        opts.index = this.useIndex;
    }

    if(this.filter){
        Object.keys(this.filter).forEach(function(key){
            var f = new Filter(schema, key, this.filter[key]);
            opts.rangeKeyCondition = f.export();
        }.bind(this));
    }
    log.silly('Calling query for `' + this.alias + '`');

    return this.model.query(this.alias, this.hash, opts);
};

// module.exports.query('song')
//     // .where('id', 'IN', [1, 2, 3])
//     // .where('loves', '<', 1)
//     // .where('trending', '=', null)
//     // .where('title', '!=', null)
//     // .where('bio', 'NULL')
//     // .where('location', 'NOT_NULL')
//     // .where('started', 'BETWEEN', [1990, 1992])
//     // .fetch(done);
Query.prototype.where = function(key, operator, value){
    operator = expandOperator(operator) || operator;
    var f = {};
    f[operator] = value;
    this.filter[key] = f;
    return this;
};

function expandOperator(op){
    switch(op){
    case '>':
        return 'GT';

    case '>=':
        return 'GE';

    case '<':
        return 'LT';

    case '<=':
        return 'LE';

    case '!=':
        return 'NE';

    case '=' || '==' || '===':
        return 'EQ';

    default:
        return undefined;
    }
}

var opToArgLength = {
    'EQ': 1,
    'NE': 1,
    'LE': 1,
    'LT': 1,
    'GE': 1,
    'GT': 1,
    'NOT_NULL': 0,
    'NULL': 0,
    'CONTAINS': 1,
    'NOT_CONTAINS': 1,
    'BEGINS_WITH': 1,
    'IN': null,
    'BETWEEN': 2
};

function Filter(schema, key, data){
    this.schema = schema;
    this.field = schema.field(key);
    this.operator = undefined;
    this.value = [];

    if(data === new Object(data)){
        this.operator = Object.keys(data)[0];
        this.value = data[this.operator];
        if(!Array.isArray(this.value)){
            this.value = [this.value];
        }
    }
    else {
        this.operator = data;
        // @todo (lucas) Assert its actually a valueles condition.
    }
    var expectLen = opToArgLength[this.operator];
    if(expectLen !== null && this.value.length !== expectLen){
        throw new Error('Invalid number of args for ' +
            key + ' filter: ' +this.operator + ' ' + this.value);
    }
}

Filter.prototype.export = function(){
    var ret = {
        'AttributeValueList': [],
        'ComparisonOperator': 'EQ'
    };
    if(this.value){
        ret.AttributeValueList = this.value.map(function(val){
            var ret = {};
            ret[this.field.shortType] = this.field.export(val);
            return ret;
        }.bind(this));
    }
    if(this.operator){
        ret.ComparisonOperator = this.operator;
    }

    return ret;
};

module.exports = Query;<|endoftext|>


<commit_before>var _ = require('lodash')
var http = require('http')

module.exports = function (model, filter, options) {
  var queryOptions = {
    protected: ['skip', 'limit', 'sort', 'distinct', 'populate', 'select', 'lean', 'query', 'projection'],
    current: {}
  }

  // H+ - JSON query param parser, exposes OR, AND and WHERE methods
  // TODO - improve to serve recursive logical operators
  function jsonQueryParser (key, value) {
    if (_.isString(value)) {
      if (value[0] === '~') { // parse RegExp
        return new RegExp(value.substring(1), 'i')
      } else if (value[0] === '>') {
        if (value[1] === '=') {
          return {$gte: value.substr(2)}
        } else {
          return {$gt: value.substr(1)}
        }
      } else if (value[0] === '<') {
        if (value[1] === '=') {
          return {$lte: value.substr(2)}
        } else {
          return {$lt: value.substr(1)}
        }
      } else if (value[0] === '!' && value[1] === '=') {
        return {$ne: value.substr(2)}
      } else if (value[0] === '=') {
        return {$eq: value.substring(1)}
      }
    } else if (_.isArray(value)) {
      if (model.schema.paths.hasOwnProperty(key)) {
        return {$in: value}
      }
    }

    return value
  }

  function build (query, req) {
    var excludedarr = filter.getExcluded(req.access)
    var arr, i, re

    for (var key in queryOptions.clean) {
      if (excludedarr.indexOf(key) !== -1) {
        // caller tries to query for excluded keys. for security
        // reasons, we will skip the first -1 objects (to provoke
        // an error) and immediately return
        return query.skip(-1)
      }

      query.where(key)
      var value = queryOptions.clean[key]

      if (value[0] === '~') {
        re = new RegExp(value.substring(1), 'i')
        query.where(key).regex(re)
      } else if (value[0] === '>') {
        if (value[1] === '=') {
          query.gte(value.substr(2))
        } else {
          query.gt(value.substr(1))
        }
      } else if (value[0] === '<') {
        if (value[1] === '=') {
          query.lte(value.substr(2))
        } else {
          query.lt(value.substr(1))
        }
      } else if (value[0] === '!' && value[1] === '=') { // H+ for !=
        query.ne(value.substr(2))
      } else if (value[0] === '[' && value[value.length - 1] === ']') {
        query.in(value.substr(1, value.length - 2).split(','))
      } else if (value[0] === '=') {
        query.equals(value.substring(1))
      } else {
        query.equals(value)
      }
    }

    // H+ exposes Query AND, OR and WHERE methods
    if (queryOptions.current.query) {
      query.where(JSON.parse(queryOptions.current.query, jsonQueryParser))
    }

    if (queryOptions.current.skip) {
      query.skip(queryOptions.current.skip)
    }

    if (options.limit && query.op !== 'count' && (!queryOptions.current.limit || queryOptions.current.limit > options.limit)) {
      queryOptions.current.limit = options.limit
    }

    if (queryOptions.current.limit) {
      query.limit(queryOptions.current.limit)
    }

    if (queryOptions.current.sort) {
      try {
        query.sort(JSON.parse(queryOptions.current.sort))
      } catch (e) {
        query.sort(queryOptions.current.sort)
      }
    }

    var selectObj = {
      root: {}
    }

    if (queryOptions.current.select) {
      arr = queryOptions.current.select.split(',')

      for (i = 0; i < arr.length; ++i) {
        var selectItem = arr[i]
        var selectionModifier = 1

        if (selectItem.match(/^\-/)) { // exclusion
          selectItem = selectItem.substring(1)
          selectionModifier = 0
        }

        if (selectItem.match(/\./)) {
          var subSelect = selectItem.split('.')

          if (!selectObj[subSelect[0]]) {
            selectObj[subSelect[0]] = {}
          }

          selectObj[subSelect[0]][subSelect[1]] = selectionModifier
        } else {
          selectObj.root[selectItem] = selectionModifier
        }
      }

      query = query.select(selectObj.root)
    }

    if (queryOptions.current.populate) {
      arr = queryOptions.current.populate.split(',')

      for (i = 0; i < arr.length; ++i) {
        if (!_.isUndefined(selectObj[arr[i]]) && !_.isEmpty(selectObj.root)) {
          selectObj.root[arr[i]] = 1
        }

        query = query.populate(arr[i], selectObj[arr[i]])
      }

      query.select(selectObj.root)
    }

    if (queryOptions.current.projection) {
      query.select(JSON.parse(queryOptions.current.projection, jsonQueryParser))
    }

    if (queryOptions.current.distinct) {
      query.distinct(queryOptions.current.distinct)
    }

    return query
  }

  function clean (req, res, next) {
    var err

    queryOptions.current = {}
    queryOptions.clean = _.clone(req.query)

    for (var key in queryOptions.clean) {
      if (queryOptions.protected.indexOf(key) !== -1) {
        queryOptions.current[key] = queryOptions.clean[key]
        delete queryOptions.clean[key]
      } else if (!model.schema.paths.hasOwnProperty(key)) {
        var keys = key.match('.') && !model.schema.paths.hasOwnProperty(key) ? key.split('.') : false

        if (!keys || !model.schema.paths.hasOwnProperty(keys[0]) || !model.schema.paths[keys[0]].schema.paths.hasOwnProperty(keys[1]) || keys.length > 2) {
          if (!model.schema.virtuals.hasOwnProperty(keys[0])) {
            err = new Error(http.STATUS_CODES[400])
            err.description = 'model does not have any property' + keys[0]
            err.status = 400
            break
          }
        }
      }
    }

    next(err)
  }

  return {
    build: build,
    clean: clean,
    current: queryOptions.current
  }
}
<commit_msg>refactor: drop support for querying with parameters<commit_after>var _ = require('lodash')
var http = require('http')

module.exports = function (model, filter, options) {
  var queryOptions = {
    protected: ['skip', 'limit', 'sort', 'distinct', 'populate', 'select', 'lean', 'query', 'projection'],
    current: {}
  }

  // H+ - JSON query param parser, exposes OR, AND and WHERE methods
  // TODO - improve to serve recursive logical operators
  function jsonQueryParser (key, value) {
    if (_.isString(value)) {
      if (value[0] === '~') { // parse RegExp
        return new RegExp(value.substring(1), 'i')
      } else if (value[0] === '>') {
        if (value[1] === '=') {
          return {$gte: value.substr(2)}
        } else {
          return {$gt: value.substr(1)}
        }
      } else if (value[0] === '<') {
        if (value[1] === '=') {
          return {$lte: value.substr(2)}
        } else {
          return {$lt: value.substr(1)}
        }
      } else if (value[0] === '!' && value[1] === '=') {
        return {$ne: value.substr(2)}
      } else if (value[0] === '=') {
        return {$eq: value.substring(1)}
      }
    } else if (_.isArray(value)) {
      if (model.schema.paths.hasOwnProperty(key)) {
        return {$in: value}
      }
    }

    return value
  }

  function build (query, req) {
    var excludedarr = filter.getExcluded(req.access)
    var arr, i, re

    for (var key in queryOptions.clean) {
      if (excludedarr.indexOf(key) !== -1) {
        // caller tries to query for excluded keys. for security
        // reasons, we will skip the first -1 objects (to provoke
        // an error) and immediately return
        return query.skip(-1)
      }

      query.where(key)
      var value = queryOptions.clean[key]

      if (value[0] === '~') {
        re = new RegExp(value.substring(1), 'i')
        query.where(key).regex(re)
      } else if (value[0] === '>') {
        if (value[1] === '=') {
          query.gte(value.substr(2))
        } else {
          query.gt(value.substr(1))
        }
      } else if (value[0] === '<') {
        if (value[1] === '=') {
          query.lte(value.substr(2))
        } else {
          query.lt(value.substr(1))
        }
      } else if (value[0] === '!' && value[1] === '=') { // H+ for !=
        query.ne(value.substr(2))
      } else if (value[0] === '[' && value[value.length - 1] === ']') {
        query.in(value.substr(1, value.length - 2).split(','))
      } else if (value[0] === '=') {
        query.equals(value.substring(1))
      } else {
        query.equals(value)
      }
    }

    // H+ exposes Query AND, OR and WHERE methods
    if (queryOptions.current.query) {
      query.where(JSON.parse(queryOptions.current.query, jsonQueryParser))
    }

    if (queryOptions.current.skip) {
      query.skip(queryOptions.current.skip)
    }

    if (options.limit && query.op !== 'count' && (!queryOptions.current.limit || queryOptions.current.limit > options.limit)) {
      queryOptions.current.limit = options.limit
    }

    if (queryOptions.current.limit) {
      query.limit(queryOptions.current.limit)
    }

    if (queryOptions.current.sort) {
      try {
        query.sort(JSON.parse(queryOptions.current.sort))
      } catch (e) {
        query.sort(queryOptions.current.sort)
      }
    }

    var selectObj = {
      root: {}
    }

    if (queryOptions.current.select) {
      arr = queryOptions.current.select.split(',')

      for (i = 0; i < arr.length; ++i) {
        var selectItem = arr[i]
        var selectionModifier = 1

        if (selectItem.match(/^\-/)) { // exclusion
          selectItem = selectItem.substring(1)
          selectionModifier = 0
        }

        if (selectItem.match(/\./)) {
          var subSelect = selectItem.split('.')

          if (!selectObj[subSelect[0]]) {
            selectObj[subSelect[0]] = {}
          }

          selectObj[subSelect[0]][subSelect[1]] = selectionModifier
        } else {
          selectObj.root[selectItem] = selectionModifier
        }
      }

      query = query.select(selectObj.root)
    }

    if (queryOptions.current.populate) {
      arr = queryOptions.current.populate.split(',')

      for (i = 0; i < arr.length; ++i) {
        if (!_.isUndefined(selectObj[arr[i]]) && !_.isEmpty(selectObj.root)) {
          selectObj.root[arr[i]] = 1
        }

        query = query.populate(arr[i], selectObj[arr[i]])
      }

      query.select(selectObj.root)
    }

    if (queryOptions.current.projection) {
      query.select(JSON.parse(queryOptions.current.projection, jsonQueryParser))
    }

    if (queryOptions.current.distinct) {
      query.distinct(queryOptions.current.distinct)
    }

    return query
  }

  function clean (req, res, next) {
    queryOptions.current = {}

    for (var key in queryOptions.clean) {
      if (queryOptions.protected.indexOf(key) !== -1) {
        queryOptions.current[key] = queryOptions.clean[key]
      }
    }

    next()
  }

  return {
    build: build,
    clean: clean,
    current: queryOptions.current
  }
}
<|endoftext|>


<commit_before>var geohash = require('ngeohash');

var cache = require('./cache');

var rangeIndex = [
    0.6, //52
    1, //50
    2.19, //48
    4.57, //46
    9.34, //44
    14.4, //42
    33.18, //40
    62.1, //38
    128.55, //36
    252.9, //34
    510.02, //32
    1015.8, //30
    2236.5, //28
    3866.9, //26
    8749.7, //24
    15664, //22
    33163.5, //20
    72226.3, //18
    150350, //16
    306600, //14
    474640, //12
    1099600, //10
    2349600, //8
    4849600, //6
    10018863 //4
];

function checkCache(hash, radiusBitDepth) {
    if (cache[radiusBitDepth][hash] !== undefined) {
        return cache[radiusBitDepth][hash];
    }
    return false;
}

function rangeDepth(radius) {
    for (var i = 0; i < rangeIndex.length - 1; i++) {
        if (radius - rangeIndex[i] < rangeIndex[i + 1] - radius) {
            return 52 - (i * 2);
        }
    }
    return 2;
}

function getQueryRangesFromRadius(lat, lon, radius, caching) {
    return getQueryRangesFromBitDepth(lat, lon, rangeDepth(radius), 52, caching);
}

function getQueryRangesFromBitDepth(lat, lon, radiusBitDepth, bitDepth, caching) {

    var hash = geohash.encode_int(lat, lon, radiusBitDepth);

    if (caching === true) {
        var cached = checkCache(lat, lon, radiusBitDepth);
        if (cached !== false) {
            return cached;
        }
    }

    var neighbors = buildBoxSet(hash, radiusBitDepth);
    var ranges = rangesFromBoxSet(neighbors);

    increaseRangeBitDepth(ranges, radiusBitDepth, bitDepth);

    if (caching === true) {
        cache[radiusBitDepth][hash] = ranges;
    }

    return ranges;
}

function buildBoxSet(hash, radiusBitDepth) {

    var neighbors = geohash.neighbors_int(hash, radiusBitDepth);

    neighbors.push(hash);
    neighbors.sort();

    if (radiusBitDepth <= 4) {
        neighbors = getUniqueInArray(neighbors);
    }

    return neighbors;
}

function rangesFromBoxSet(neighbors) {

    var ranges = [],
        lowerRange = 0,
        upperRange = 0;

    for (var i = 0; i < neighbors.length; i++) {
        lowerRange = neighbors[i];
        upperRange = lowerRange + 1;
        while (neighbors[i + 1] === upperRange) {
            neighbors.shift();
            upperRange = neighbors[i] + 1;
        }
        ranges.push([lowerRange, upperRange]);
    }

    return ranges;
}

function increaseRangeBitDepth(ranges, radiusBitDepth, bitDepth) {

    var bitDiff = bitDepth - radiusBitDepth;
    var range;

    for (var i = 0; i < ranges.length; i++) {
        range = ranges[i];
        range[0] = leftShift(range[0], bitDiff);
        range[1] = leftShift(range[1], bitDiff);
    }
}

// helper methods

function leftShift(integer, shft) {
    return integer * Math.pow(2, shft);
}

function getUniqueInArray(arr) {
    var u = {},
        a = [];

    for (var i = 0; i < arr.length; ++i) {
        if (u[arr[i]] === 1) {
            continue;
        }
        a.push(arr[i]);
        u[arr[i]] = 1;
    }

    return a;
}

module.exports = exports = getQueryRangesFromRadius;
<commit_msg>Formatting.<commit_after>var geohash = require('ngeohash');

var cache = require('./cache');

var rangeIndex = [
    0.6, // 52
    1, // 50
    2.19, // 48
    4.57, // 46
    9.34, // 44
    14.4, // 42
    33.18, // 40
    62.1, // 38
    128.55, // 36
    252.9, // 34
    510.02, // 32
    1015.8, // 30
    2236.5, // 28
    3866.9, // 26
    8749.7, // 24
    15664, // 22
    33163.5, // 20
    72226.3, // 18
    150350, // 16
    306600, // 14
    474640, // 12
    1099600, // 10
    2349600, // 8
    4849600, // 6
    10018863 // 4
];

function checkCache(hash, radiusBitDepth) {
    if (cache[radiusBitDepth][hash] !== undefined) {
        return cache[radiusBitDepth][hash];
    }
    return false;
}

function rangeDepth(radius) {
    var i;
    for (i = 0; i < rangeIndex.length - 1; i++) {
        if (radius - rangeIndex[i] < rangeIndex[i + 1] - radius) {
            return 52 - (i * 2);
        }
    }
    return 2;
}

function getQueryRangesFromRadius(lat, lon, radius, caching) {
    return getQueryRangesFromBitDepth(lat, lon, rangeDepth(radius), 52, caching);
}

function getQueryRangesFromBitDepth(lat, lon, radiusBitDepth, bitDepth, caching) {

    var hash = geohash.encode_int(lat, lon, radiusBitDepth);
    var cached;
    var neighbors;
    var ranges;

    if (caching === true) {
        cached = checkCache(lat, lon, radiusBitDepth);
        if (cached !== false) {
            return cached;
        }
    }

    neighbors = buildBoxSet(hash, radiusBitDepth);
    ranges = rangesFromBoxSet(neighbors);

    increaseRangeBitDepth(ranges, radiusBitDepth, bitDepth);

    if (caching === true) {
        cache[radiusBitDepth][hash] = ranges;
    }

    return ranges;
}

function buildBoxSet(hash, radiusBitDepth) {

    var neighbors = geohash.neighbors_int(hash, radiusBitDepth);

    neighbors.push(hash);
    neighbors.sort();

    if (radiusBitDepth <= 4) {
        neighbors = getUniqueInArray(neighbors);
    }

    return neighbors;
}

function rangesFromBoxSet(neighbors) {

    var ranges = [];
    var lowerRange = 0;
    var upperRange = 0;
    var i;

    for (i = 0; i < neighbors.length; i++) {
        lowerRange = neighbors[i];
        upperRange = lowerRange + 1;
        while (neighbors[i + 1] === upperRange) {
            neighbors.shift();
            upperRange = neighbors[i] + 1;
        }
        ranges.push([lowerRange, upperRange]);
    }

    return ranges;
}

function increaseRangeBitDepth(ranges, radiusBitDepth, bitDepth) {

    var bitDiff = bitDepth - radiusBitDepth;
    var range;
    var i;

    for (i = 0; i < ranges.length; i++) {
        range = ranges[i];
        range[0] = leftShift(range[0], bitDiff);
        range[1] = leftShift(range[1], bitDiff);
    }
}

// helper methods

function leftShift(integer, shft) {
    return integer * Math.pow(2, shft);
}

function getUniqueInArray(arr) {
    var u = {};
    var a = [];
    var i;

    for (i = 0; i < arr.length; ++i) {
        if (u[arr[i]] === 1) {
            continue;
        }
        a.push(arr[i]);
        u[arr[i]] = 1;
    }

    return a;
}

module.exports = exports = getQueryRangesFromRadius;
<|endoftext|>


<commit_before>// please ignore any magic, magic can be resolved at xia.umd@gmail.com

var nodemailer = require('nodemailer');
var Q = require('q');
var config = require('./config.js');
var knex = require('knex')({
  dialect: 'mysql',
  connection: config.mysql,
  debug: false
});

GetYesterdayMentions().then(function(mentions){

    var output = '
';
    for(var i in mentions) {
        output +=   '
' +
                    '' + mentions[i].framework + '	' +
                    ''+ mentions[i].count + '	' +
                    '
'
    }
    output += '
'

    var mailOptions = config.mailman;
    mailOptions.subject = 'Twitter Mentions ' + _yesterdayDate(),

    // send mail with defined transport object
    transporter.sendMail(mailOptions, function(error, info){
        if(error){
            console.log(error);
        }else{
            console.log('Message sent: ' + info.response);
            process.exit(1);
        }
    });
});

// create reusable transporter object using SMTP transport
var transporter = nodemailer.createTransport({
    service: 'Gmail',
    auth: config.gmailAuth
});

function GetYesterdayMentions() {
    var today = new Date();
    var start = new Date(1900 + today.getYear(), today.getMonth(), today.getDate()-1, 1).getTime() * 0.001;
    var end = new Date(1900 + today.getYear(), today.getMonth(), today.getDate(), 1).getTime() * 0.001;
    return knex('tweets')
    .distinct('flag').select()
    .then(function(targets){
        var promises = [];
        for (var index in targets){
            promises.push(_countMentions(targets[index].flag, start, end));
        }
        return Q.all(promises);
    });
}

function _yesterdayDate(){
    var today = new Date();
    //javascript has date values 0 indexed lol.
    return (today.getMonth()+1) + "/" + (today.getDate()-1);
}

function _countMentions(framework, start, end) {
    return knex('tweets').count('*')
    .where('created_at', '>', start)
    .where('created_at', '<', end)
    .where('flag', '=', framework)
    .then(function(row){
        return {
            count: row[0]['count(*)'],
            framework: framework
        }
    });
}
<commit_msg>html option too woops<commit_after>// please ignore any magic, magic can be resolved at xia.umd@gmail.com

var nodemailer = require('nodemailer');
var Q = require('q');
var config = require('./config.js');
var knex = require('knex')({
  dialect: 'mysql',
  connection: config.mysql,
  debug: false
});

GetYesterdayMentions().then(function(mentions){

    var output = '
';
    for(var i in mentions) {
        output +=   '
' +
                    '' + mentions[i].framework + '	' +
                    ''+ mentions[i].count + '	' +
                    '
'
    }
    output += '
'

    var mailOptions = config.mailman;
    mailOptions.subject = 'Twitter Mentions ' + _yesterdayDate();
    mailOptions.html = output;

    // send mail with defined transport object
    transporter.sendMail(mailOptions, function(error, info){
        if(error){
            console.log(error);
        }else{
            console.log('Message sent: ' + info.response);
            process.exit(1);
        }
    });
});

// create reusable transporter object using SMTP transport
var transporter = nodemailer.createTransport({
    service: 'Gmail',
    auth: config.gmailAuth
});

function GetYesterdayMentions() {
    var today = new Date();
    var start = new Date(1900 + today.getYear(), today.getMonth(), today.getDate()-1, 1).getTime() * 0.001;
    var end = new Date(1900 + today.getYear(), today.getMonth(), today.getDate(), 1).getTime() * 0.001;
    return knex('tweets')
    .distinct('flag').select()
    .then(function(targets){
        var promises = [];
        for (var index in targets){
            promises.push(_countMentions(targets[index].flag, start, end));
        }
        return Q.all(promises);
    });
}

function _yesterdayDate(){
    var today = new Date();
    //javascript has date values 0 indexed lol.
    return (today.getMonth()+1) + "/" + (today.getDate()-1);
}

function _countMentions(framework, start, end) {
    return knex('tweets').count('*')
    .where('created_at', '>', start)
    .where('created_at', '<', end)
    .where('flag', '=', framework)
    .then(function(row){
        return {
            count: row[0]['count(*)'],
            framework: framework
        }
    });
}
<|endoftext|>


<commit_before>// ==UserScript==
// @name        hvShrine
// @name:zh-CN  【HV】祭坛
// @author      Dodying
// @namespace   https://github.com/dodying/Dodying-UserJs
// @supportURL  https://github.com/dodying/Dodying-UserJs/issues
// @icon        http://cdn4.iconfinder.com/data/icons/mood-smiles/80/mood-29-48.png
// @include     http*://hentaiverse.org/?s=Bazaar&ss=ss
// @include     http*://alt.hentaiverse.org/?s=Bazaar&ss=ss
// @version     1.01a
// @grant       none
// @run-at      document-end
// ==/UserScript==
main();
function main() {
  if (document.querySelector('.idp').innerText !== 'Precursor Artifact') return;
  var id = document.querySelector('.idp').getAttribute('id').replace('item_pane', '');
  if (id < 30000) {
    xhr(id, '0');
  } else {
    /*
  1 One-Handed
  2 Two-Handed
  3 Staffs
  4 Shields
  5 Cloth Armor
  6 Light Armor
  7 Heavy Armor
  */
    xhr(id, '5');
  }
}
function xhr(id, reward) {
  var xhr = new XMLHttpRequest();
  xhr.open('POST', window.location.href);
  xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8');
  xhr.responseType = 'document';
  var parm = 'select_item=' + id + '&select_reward=' + reward;
  xhr.send(parm);
  xhr.onload = function (e) {
    var data = e.target.response;
    var messageBoxNew = data.querySelector('#messagebox');
    messageBoxNew.innerHTML = messageBoxNew.innerHTML.replace(/Exquisite/g, '5精致的').replace(/Magnificent/g, '6华丽的').replace(/Legendary/g, '7传奇的').replace(/Peerless/g, '8无双的');
    if (document.querySelector('#messagebox')) {
      var messageBox = document.querySelector('#messagebox');
      messageBox.parentNode.replaceChild(messageBoxNew, messageBox);
    } else {
      document.body.appendChild(messageBoxNew);
    }
    var itemBoxNew = data.querySelector('#item_pane');
    var itemBox = document.querySelector('#item_pane');
    itemBox.parentNode.replaceChild(itemBoxNew, itemBox);
    if (document.querySelector('.idp')) main();
  }
}
<commit_msg>Delete hvShrine.user.js<commit_after><|endoftext|>