<commit_before>Animation.ScrollDelegate = function (init) {
    this._state = undefined;

    this._stepper = null;
    this._stepsize = null;

    // max state values (when going forward or backward)
    this.maxForward = 0;
    this.maxBackward = 0;

    this._animation = null;
    this.value0 = 0;

    this.tiles = null;
    this.tileValue0 = 0;

    this.FORWARD_BUFFER = 0;
    this.BACKWARD_BUFFER = 0;

    // i don't like this init function...
    if (typeof init === 'function') init.apply(this);
};

Animation.ScrollDelegate.prototype = {

    /**
     * return {Number} positive number
     */
    _stepsize: function (state) {
        throw new Error('stepsize method not implemented');
    },

    set stepsize(stepsize) {
        this._stepsize = stepsize;
        switch (typeof stepsize) {
        case 'number':
            this._stepper = Animation.ScrollDelegate.CONST_STEPPER;
            break;
        case 'function':
            this._stepper = Animation.ScrollDelegate.CHANGING_STEPPER;
            break;
        default:
            throw new Error("setpsize must either be number or function");
        }
    },

    get stateByValue() {return this._stepper.stateByValue;},
    get valueByState() {return this._stepper.valueByState;},
    get positionTile() {return this._stepper.positionTile;},

    animationReachedPoint: function (animation, value) {

        var state = this.stateByValue(value),
            destState = this.stateByValue(animation.dest),
            nextState;

        // TODO not nice: this assumes that we must reach each
        // step point, but it only makes sense for buffersize eq. 1
        // what about nextState = state + buffer?
        if (destState > state) {
            nextState = (Math.floor(state) + 1);
            nextState = Math.min(nextState, destState);
        }
        else if (destState < state) {
            nextState = (Math.ceil(state) - 1);
            nextState = Math.max(nextState, destState);
        }
        else {
            nextState = state;
        }

        if (state === state >> 0)
            this.__changeState(animation, state, nextState);

        return this.valueByState(nextState);
    },

    __changeState: function (animation, state, nextState) {
        var newState = undefined;

        if (this._state === undefined) {
            newState = state;
        }
        else if (nextState > this._state + this.FORWARD_BUFFER) {
            newState = nextState - this.FORWARD_BUFFER;
        }
        else if (nextState < this._state - this.BACKWARD_BUFFER) {
            newState = nextState + this.BACKWARD_BUFFER;
        }

        if (newState !== undefined) {
            // TODO shall the state be set here? so that .drawTile sees
            // proper _state value?
            this.animationEnteredState(animation, newState, this._state);
            this._state = newState;
        }
    },

    // scroll

    animationEnteredState: function (animation, state, prev) {
        this.draw(state);
    },

    draw: function (state) {
        var numtiles = this.tiles.length,
            shift = -state % numtiles,
            pos0 = (-state / numtiles >> 0) * numtiles;

        Array.prototype.forEach.call(this.tiles, function (tile, i) {
            // i += this.FORWARD_BUFFER;
            var correction = shift > i ? numtiles : 0,
                pos = pos0 + i + correction;
            // position tile
            this.positionTile(tile, pos);
            // draw tile
            this.drawTile(tile, pos); // TODO needs update
        }, this);
    },

    redraw: function () {this.draw(this._state);},

    set animation(animation) {
        this._animation = animation;
        this._animation.delegate = this;
        this.value0 = animation.volatileValue();
        this.tileValue0 = animation.volatileValue.call({
            el: this.tiles[0],
            property: animation.property
        });
    },

    forward: function () {return this.scrollBy(1);},
    backward: function () {return this.scrollBy(-1);},

    scrollTo: function (dest) {
        dest = Math.min(dest, this.maxForward);
        dest = Math.max(dest, -this.maxBackward);
        if (dest !== this.dest) {
            this._animation.runTo(this.valueByState(dest));
            return true;
        }
        return false;
    },

    setTo: function (state) {
        this._animation.setTo(this.valueByState(state));
        this.redraw();
    },

    scrollBy: function (steps) {
        return this.scrollTo(this.dest + steps);
    },

    drawTile: function (tile, pos) {
        throw new Error(".drawTile missing implementation");
    },

    get dest() {
        return this.stateByValue(this._animation.dest);
    },

    // ?
    get el() {return this._animation.el;},

    /**
     * Get tile element by it's visibile position
     */
    tile: function (vpos) {
        return this.tiles[(-this.dest + vpos) % this.tiles.length];
    },

    toString: function () {return "[object Animation.ScrollDelegate]";}
};

Animation.ScrollDelegate.CONST_STEPPER = {
    stateByValue: function (value) {
        return (value - this.value0) / this._stepsize;
    },

    valueByState: function (state) {
        return this.value0 + state * this._stepsize;
    },

    positionTile: function (tile, pos) {
        tile.style[this._animation.property] = this.tileValue0 +
            pos * this._stepsize + this._animation.unit;
    }
};

Animation.ScrollDelegate.CHANGING_STEPPER = {
    stateByValue: function (value) {
        // (value - value0) / STEP

        value -= this.value0;

        // assume all 'steps' are > 0
        var sign = value > 0 ? 1 : -1;

        value = value > 0 ? value : -value; // abs

        var state = 0, step;
        while (true) {
            step = this._stepsize(state);
            if (value - step >= 0) {
                value -= step;
                state += 1;
            }
            else {
                state += value / step;
                return state * sign;
            }
        }
    },

    valueByState: function (state) {
        // value0 + state * STEP

        var sign = state > 0 ? 1 : -1,
            value = 0;

        // compute 'state * STEP'
        state = state > 0 ? state : -state;
        var i = 0;
        while (i < state) {
            value += this._stepsize(i++);
        }
        if (i > state) {
            value += (state - (i - 1)) * this._stepsize(i);
        }
        return this.value0 + sign * value;
    },

    positionTile: function (tile, pos) {
        var value = this.tileValue0;
        assert(pos >= 0, "pos < 0 is not yet handled, sorry!");
        // var sign = pos > 0 ? 1 : -1;
        // pos *= sign;
        // TODO remember last (pos, value) and compute from that point!
        while (pos--) {
            value += this._stepsize(pos); // * sign;
        }
        tile.style[this._animation.property] = value + this._animation.unit;
    }
};
<commit_msg>more robust stepsize loop<commit_after>Animation.ScrollDelegate = function (init) {
    this._state = undefined;

    this._stepper = null;
    this._stepsize = null;

    // max state values (when going forward or backward)
    this.maxForward = 0;
    this.maxBackward = 0;

    this._animation = null;
    this.value0 = 0;

    this.tiles = null;
    this.tileValue0 = 0;

    this.FORWARD_BUFFER = 0;
    this.BACKWARD_BUFFER = 0;

    // i don't like this init function...
    if (typeof init === 'function') init.apply(this);
};

Animation.ScrollDelegate.prototype = {

    /**
     * return {Number} positive number
     */
    _stepsize: function (state) {
        throw new Error('stepsize method not implemented');
    },

    set stepsize(stepsize) {
        this._stepsize = stepsize;
        switch (typeof stepsize) {
        case 'number':
            this._stepper = Animation.ScrollDelegate.CONST_STEPPER;
            break;
        case 'function':
            this._stepper = Animation.ScrollDelegate.CHANGING_STEPPER;
            break;
        default:
            throw new Error("setpsize must either be number or function");
        }
    },

    get stateByValue() {return this._stepper.stateByValue;},
    get valueByState() {return this._stepper.valueByState;},
    get positionTile() {return this._stepper.positionTile;},

    animationReachedPoint: function (animation, value) {
        var state = this.stateByValue(value),
            destState = this.stateByValue(animation.dest),
            nextState;
        // TODO not nice: this assumes that we must reach each
        // step point, but it only makes sense for buffersize eq. 1
        // what about nextState = state + buffer?
        if (destState > state) {
            nextState = (Math.floor(state) + 1);
            nextState = Math.min(nextState, destState);
        }
        else if (destState < state) {
            nextState = (Math.ceil(state) - 1);
            nextState = Math.max(nextState, destState);
        }
        else {
            nextState = state;
        }

        if (state === state >> 0) {
            this.__changeState(animation, state, nextState);
        }

        return this.valueByState(nextState);
    },

    __changeState: function (animation, state, nextState) {
        var newState = undefined;

        if (this._state === undefined) {
            newState = state;
        }
        else if (nextState > this._state + this.FORWARD_BUFFER) {
            newState = nextState - this.FORWARD_BUFFER;
        }
        else if (nextState < this._state - this.BACKWARD_BUFFER) {
            newState = nextState + this.BACKWARD_BUFFER;
        }

        if (newState !== undefined) {
            // TODO shall the state be set here? so that .drawTile sees
            // proper _state value?
            this.animationEnteredState(animation, newState, this._state);
            this._state = newState;
        }
    },

    // scroll

    animationEnteredState: function (animation, state, prev) {
        this.draw(state);
    },

    draw: function (state) {
        var numtiles = this.tiles.length,
            shift = -state % numtiles,
            pos0 = (-state / numtiles >> 0) * numtiles;

        Array.prototype.forEach.call(this.tiles, function (tile, i) {
            // i += this.FORWARD_BUFFER;
            var correction = shift > i ? numtiles : 0,
                pos = pos0 + i + correction;
            // position tile
            this.positionTile(tile, pos);
            // draw tile
            this.drawTile(tile, pos); // TODO needs update
        }, this);
    },

    redraw: function () {this.draw(this._state);},

    set animation(animation) {
        this._animation = animation;
        this._animation.delegate = this;
        this.value0 = animation.volatileValue();
        this.tileValue0 = animation.volatileValue.call({
            el: this.tiles[0],
            property: animation.property
        });
    },

    forward: function () {return this.scrollBy(1);},
    backward: function () {return this.scrollBy(-1);},

    scrollTo: function (dest) {
        dest = Math.min(dest, this.maxForward);
        dest = Math.max(dest, -this.maxBackward);
        if (dest !== this.dest) {
            this._animation.runTo(this.valueByState(dest));
            return true;
        }
        return false;
    },

    setTo: function (state) {
        this._animation.setTo(this.valueByState(state));
        this.redraw();
    },

    scrollBy: function (steps) {
        return this.scrollTo(this.dest + steps);
    },

    drawTile: function (tile, pos) {
        throw new Error(".drawTile missing implementation");
    },

    get dest() {
        return this.stateByValue(this._animation.dest);
    },

    // ?
    get el() {return this._animation.el;},

    /**
     * Get tile element by it's visibile position
     */
    tile: function (vpos) {
        return this.tiles[(-this.dest + vpos) % this.tiles.length];
    },

    toString: function () {return "[object Animation.ScrollDelegate]";}
};

Animation.ScrollDelegate.CONST_STEPPER = {
    stateByValue: function (value) {
        return (value - this.value0) / this._stepsize;
    },

    valueByState: function (state) {
        return this.value0 + state * this._stepsize;
    },

    positionTile: function (tile, pos) {
        tile.style[this._animation.property] = this.tileValue0 +
            pos * this._stepsize + this._animation.unit;
    }
};

Animation.ScrollDelegate.CHANGING_STEPPER = {
    stateByValue: function (value) {
        // (value - value0) / STEP

        value -= this.value0;

        // assume all 'steps' are > 0
        var sign = 1;
        if (value <= 0) {
            value = -value; // abs
            sign = -1;
        }

        var state = 0, step;

        // loop invariant: value >= 0
        while (true) {
            step = this._stepsize(state);
            if (value === 0) {
                return state * sign;
            }
            else if (value - step > 0) {
                value -= step;
                state += 1;
            }
            else {
                state += value / step;
                return state * sign;
            }
        }
    },

    valueByState: function (state) {
        // value0 + state * STEP

        var sign = state > 0 ? 1 : -1,
            value = 0;

        // compute 'state * STEP'
        state = state > 0 ? state : -state;
        var i = 0;
        while (i < state) {
            value += this._stepsize(i++);
        }
        if (i > state) {
            value += (state - (i - 1)) * this._stepsize(i);
        }
        return this.value0 + sign * value;
    },

    positionTile: function (tile, pos) {
        var value = this.tileValue0;
        assert(pos >= 0, "pos < 0 is not yet handled, sorry!");
        // var sign = pos > 0 ? 1 : -1;
        // pos *= sign;
        // TODO remember last (pos, value) and compute from that point!
        while (pos--) {
            value += this._stepsize(pos); // * sign;
        }
        tile.style[this._animation.property] = value + this._animation.unit;
    }
};
<|endoftext|>


<commit_before>var Seed = require('./seed');
var log = require('./log');
var dbmUtil = require('./util');
var Promise = require('bluebird');

Seeder = function (driver, seedsDir, versionControlled) {
  this.driver = driver;
  this.seedDir = seedsDir;
  this.isVC = versionControlled;
};

Seeder.prototype = {

  seed: function (argv, callback) {

    if (this.isVC)
      this.up(argv, callback);
    else
      this._staticSeed(argv.destination, callback);
  },

  up: function(funcOrOpts, callback) {
    if (dbmUtil.isFunction(funcOrOpts)) {
      funcOrOpts(this.driver, callback);
    } else {
      this.upToBy(funcOrOpts.destination, funcOrOpts.count, callback);
    }
  },

  down: function(funcOrOpts, callback) {
    if (dbmUtil.isFunction(funcOrOpts)) {
      funcOrOpts(this.driver, callback);
    } else {
      this.downToBy(funcOrOpts.count, callback);
    }
  },

  /**
    * Statically call two methods from a static seeder.
    *
    * First: cleanSeeds
    * Second: seed
    *
    * It's highly recommended to not use version controlled seeders at the same
    * time as statics. While the cleanSeeds most of the time, the user executes
    * truncates or deletes on his database. A VC-Seeder can't detect this
    * and thus the state keeps the same, even if all changes of the VC-Seeder
    * are gone.
    *
    * Nevertheless, there is a possiblity to use static seeders together with
    * VC-Seeder, if you keep everything organized well at least.
    *
    * If a single seed is linked with it's tables and databases which it got
    * applied to, the state table of the seeds will automatically cleaned up.
    *
    */
  _staticSeed: function (partialName, callback) {

    var self = this;

    Seed.loadFromFilesystem(self.seedDir, function(err, allSeeds) {

      if (err) {

        callback(err);
        return;
      }

      var toRun = dbmUtil.filterUp(allSeeds, [], partialName);

      if (toRun.length === 0) {
        log.info('No seeds to run');
        callback(null);
        return;
      }

      return Promise.resolve(toRun).each(function(seeder) {
        log.verbose('preparing to run up seeder:', seeder.name);

        return self.driver.startMigration()
          .catch(callback)
          .then(function() {

            return (Promise.promisify(seeder.up.bind(seeder)))(self.driver, true);
          });
      })
      .catch(callback)
      .nodeify(callback);
    });
  }
};

module.exports = Seeder;
<commit_msg>add end migration<commit_after>var Seed = require('./seed');
var log = require('./log');
var dbmUtil = require('./util');
var Promise = require('bluebird');

Seeder = function (driver, seedsDir, versionControlled) {
  this.driver = driver;
  this.seedDir = seedsDir;
  this.isVC = versionControlled;
};

Seeder.prototype = {

  seed: function (argv, callback) {

    if (this.isVC)
      this.up(argv, callback);
    else
      this._staticSeed(argv.destination, callback);
  },

  up: function(funcOrOpts, callback) {
    if (dbmUtil.isFunction(funcOrOpts)) {
      funcOrOpts(this.driver, callback);
    } else {
      this.upToBy(funcOrOpts.destination, funcOrOpts.count, callback);
    }
  },

  down: function(funcOrOpts, callback) {
    if (dbmUtil.isFunction(funcOrOpts)) {
      funcOrOpts(this.driver, callback);
    } else {
      this.downToBy(funcOrOpts.count, callback);
    }
  },

  /**
    * Statically call two methods from a static seeder.
    *
    * First: cleanSeeds
    * Second: seed
    *
    * It's highly recommended to not use version controlled seeders at the same
    * time as statics. While the cleanSeeds most of the time, the user executes
    * truncates or deletes on his database. A VC-Seeder can't detect this
    * and thus the state keeps the same, even if all changes of the VC-Seeder
    * are gone.
    *
    * Nevertheless, there is a possiblity to use static seeders together with
    * VC-Seeder, if you keep everything organized well at least.
    *
    * If a single seed is linked with it's tables and databases which it got
    * applied to, the state table of the seeds will automatically cleaned up.
    *
    */
  _staticSeed: function (partialName, callback) {

    var self = this;

    Seed.loadFromFilesystem(self.seedDir, function(err, allSeeds) {

      if (err) {

        callback(err);
        return;
      }

      var toRun = dbmUtil.filterUp(allSeeds, [], partialName);

      if (toRun.length === 0) {
        log.info('No seeds to run');
        callback(null);
        return;
      }

      return Promise.resolve(toRun).each(function(seeder) {
        log.verbose('preparing to run up seeder:', seeder.name);

        return self.driver.startMigration()
          .catch(callback)
          .then(function() {

            return (Promise.promisify(seeder.up.bind(seeder)))(self.driver, true);
          });
      })
      .catch(callback)
      .then(self.driver.endMigration.bind(self.driver))
      .catch(callback)
      .nodeify(callback);
    });
  }
};

module.exports = Seeder;
<|endoftext|>


<commit_before>/*!
 * node-gcm
 * Copyright(c) 2013 Marcus Farkas <toothlessgear@finitebox.com>
 * MIT Licensed
 */
var Constants = require('./constants');
var Result = require('./result');

var https = require('https');
var timer = require('timers');
var req = require('request')

function Sender(key , options) {
    this.key = key;
    this.options = options ;
}

var sendNoRetryMethod = Sender.prototype.sendNoRetry = function (message, registrationIds, callback) {
    var body = {},
        result = new Result(),
        requestBody,
        post_options,
        post_req,
        timeout;

    body[Constants.JSON_REGISTRATION_IDS] = registrationIds;

    if (message.delayWhileIdle !== undefined) {
        body[Constants.PARAM_DELAY_WHILE_IDLE] = message.delayWhileIdle;
    }
    if (message.collapseKey !== undefined) {
        body[Constants.PARAM_COLLAPSE_KEY] = message.collapseKey;
    }
    if (message.timeToLive !== undefined) {
        body[Constants.PARAM_TIME_TO_LIVE] = message.timeToLive;
    }
    if (message.dryRun !== undefined) {
        body[Constants.PARAM_DRY_RUN] = message.dryRun;
    }
    if (message.hasData) {
        body[Constants.PARAM_PAYLOAD_KEY] = message.data;
    }

    requestBody = JSON.stringify(body);

    post_options = {
        method: 'POST',
        headers: {
            'Content-Type': 'application/json',
            'Content-length': Buffer.byteLength(requestBody, 'utf8'),
            'Authorization': 'key=' + this.key
        },
        uri: Constants.GCM_SEND_URI,
        body: requestBody
    };

    if(this.options && this.options.proxy){
        post_options.proxy = this.options.proxy;
    }

    timeout = Constants.SOCKET_TIMEOUT;

    if(this.options && this.options.timeout){
        timeout =  this.options.timeout;
    }

    post_options.timeout = timeout;

    post_req = req(post_options, function (err, res, resBody) {

        if (err)
            return callback(err, null);

        if (!res)
            return callback('response is null', null);

        if (res.statusCode === 503) {
            console.log('GCM service is unavailable');
            return callback(res.statusCode, null);
        } else if(res.statusCode == 401){
            console.log('Unauthorized');
            return callback(res.statusCode, null);
        } else if (res.statusCode !== 200) {
            console.log('Invalid request: ' + res.statusCode);
            return callback(res.statusCode, null);
        }

        // Make sure that we don't crash in case something goes wrong while
        // handling the response.
        try {
            var data = JSON.parse(resBody);
        } catch (e) {
            console.log("Error handling GCM response " + e);
            return callback("error", null);
        }
        callback(null, data);
    });
};

Sender.prototype.send = function (message, registrationId, retries, callback) {

    var attempt = 1,
        backoff = Constants.BACKOFF_INITIAL_DELAY;

    if (registrationId.length === 1) {

        this.sendNoRetry(message, registrationId, function lambda(err, result) {

            if (result === undefined) {
                if (attempt < retries) {
                    var sleepTime = backoff * 2 * attempt;
                    if (sleepTime > Constants.MAX_BACKOFF_DELAY) {
                        sleepTime = Constants.MAX_BACKOFF_DELAY;
                    }
                    timer.setTimeout(function () {
                        sendNoRetryMethod(message, registrationId, lambda);
                    }, sleepTime);
                } else {
                    console.log('Could not send message after ' + retries + ' attempts');
                    callback(null, result);
                }
                attempt += 1;
            } else callback(null, result);
        });
    } else if (registrationId.length > 1) {
        this.sendNoRetry(message, registrationId, function lambda(err, result) {

            if (attempt < retries) {
                var sleepTime = backoff * 2 * attempt,
                    unsentRegIds = [],
                    i;
                if (sleepTime > Constants.MAX_BACKOFF_DELAY) {
                    sleepTime = Constants.MAX_BACKOFF_DELAY;
                }

                if (result) {
                    for (i = 0; i < registrationId.length; i += 1) {
                        if (result.results[i].error === 'Unavailable') {
                            unsentRegIds.push(registrationId[i]);
                        }
                    }
                }

                registrationId = unsentRegIds;
                if (registrationId.length !== 0) {
                    timer.setTimeout(function () {
                        sendNoRetryMethod(message, registrationId, lambda);
                    }, sleepTime);
                    attempt += 1;
                } else callback(null, result);

            } else {
                console.log('Could not send message to all devices after ' + retries + ' attempts');
                callback(null, result);
            }
        });
    } else {
        console.log('No RegistrationIds given!');
        callback('No RegistrationIds given!', null);
    }
};

module.exports = Sender;
<commit_msg>check for not result instead of result === undefined<commit_after>/*!
 * node-gcm
 * Copyright(c) 2013 Marcus Farkas <toothlessgear@finitebox.com>
 * MIT Licensed
 */
var Constants = require('./constants');
var Result = require('./result');

var https = require('https');
var timer = require('timers');
var req = require('request')

function Sender(key , options) {
    this.key = key;
    this.options = options ;
}

var sendNoRetryMethod = Sender.prototype.sendNoRetry = function (message, registrationIds, callback) {
    var body = {},
        result = new Result(),
        requestBody,
        post_options,
        post_req,
        timeout;

    body[Constants.JSON_REGISTRATION_IDS] = registrationIds;

    if (message.delayWhileIdle !== undefined) {
        body[Constants.PARAM_DELAY_WHILE_IDLE] = message.delayWhileIdle;
    }
    if (message.collapseKey !== undefined) {
        body[Constants.PARAM_COLLAPSE_KEY] = message.collapseKey;
    }
    if (message.timeToLive !== undefined) {
        body[Constants.PARAM_TIME_TO_LIVE] = message.timeToLive;
    }
    if (message.dryRun !== undefined) {
        body[Constants.PARAM_DRY_RUN] = message.dryRun;
    }
    if (message.hasData) {
        body[Constants.PARAM_PAYLOAD_KEY] = message.data;
    }

    requestBody = JSON.stringify(body);

    post_options = {
        method: 'POST',
        headers: {
            'Content-Type': 'application/json',
            'Content-length': Buffer.byteLength(requestBody, 'utf8'),
            'Authorization': 'key=' + this.key
        },
        uri: Constants.GCM_SEND_URI,
        body: requestBody
    };

    if(this.options && this.options.proxy){
        post_options.proxy = this.options.proxy;
    }

    timeout = Constants.SOCKET_TIMEOUT;

    if(this.options && this.options.timeout){
        timeout =  this.options.timeout;
    }

    post_options.timeout = timeout;

    post_req = req(post_options, function (err, res, resBody) {

        if (err)
            return callback(err, null);

        if (!res)
            return callback('response is null', null);

        if (res.statusCode === 503) {
            console.log('GCM service is unavailable');
            return callback(res.statusCode, null);
        } else if(res.statusCode == 401){
            console.log('Unauthorized');
            return callback(res.statusCode, null);
        } else if (res.statusCode !== 200) {
            console.log('Invalid request: ' + res.statusCode);
            return callback(res.statusCode, null);
        }

        // Make sure that we don't crash in case something goes wrong while
        // handling the response.
        try {
            var data = JSON.parse(resBody);
        } catch (e) {
            console.log("Error handling GCM response " + e);
            return callback("error", null);
        }
        callback(null, data);
    });
};

Sender.prototype.send = function (message, registrationId, retries, callback) {

    var attempt = 1,
        backoff = Constants.BACKOFF_INITIAL_DELAY;

    if (registrationId.length === 1) {

        this.sendNoRetry(message, registrationId, function lambda(err, result) {

            if (!result) {
                if (attempt < retries) {
                    var sleepTime = backoff * 2 * attempt;
                    if (sleepTime > Constants.MAX_BACKOFF_DELAY) {
                        sleepTime = Constants.MAX_BACKOFF_DELAY;
                    }
                    timer.setTimeout(function () {
                        sendNoRetryMethod(message, registrationId, lambda);
                    }, sleepTime);
                } else {
                    console.log('Could not send message after ' + retries + ' attempts');
                    callback(null, result);
                }
                attempt += 1;
            } else callback(null, result);
        });
    } else if (registrationId.length > 1) {
        this.sendNoRetry(message, registrationId, function lambda(err, result) {

            if (attempt < retries) {
                var sleepTime = backoff * 2 * attempt,
                    unsentRegIds = [],
                    i;
                if (sleepTime > Constants.MAX_BACKOFF_DELAY) {
                    sleepTime = Constants.MAX_BACKOFF_DELAY;
                }

                if (result) {
                    for (i = 0; i < registrationId.length; i += 1) {
                        if (result.results[i].error === 'Unavailable') {
                            unsentRegIds.push(registrationId[i]);
                        }
                    }
                }

                registrationId = unsentRegIds;
                if (registrationId.length !== 0) {
                    timer.setTimeout(function () {
                        sendNoRetryMethod(message, registrationId, lambda);
                    }, sleepTime);
                    attempt += 1;
                } else callback(null, result);

            } else {
                console.log('Could not send message to all devices after ' + retries + ' attempts');
                callback(null, result);
            }
        });
    } else {
        console.log('No RegistrationIds given!');
        callback('No RegistrationIds given!', null);
    }
};

module.exports = Sender;
<|endoftext|>


<commit_before>/*!
 * node-gcm
 * Copyright(c) 2013 Marcus Farkas <toothlessgear@finitebox.com>
 * MIT Licensed
 */
var Constants = require('./constants');
var Result = require('./result');

var https = require('https');
var timer = require('timers');
var req = require('request')

function Sender(key , options) {
    this.key = key;
    this.options = options ;
}

var sendNoRetryMethod = Sender.prototype.sendNoRetry = function (message, registrationIds, callback) {
    var body = {},
        result = new Result(),
        requestBody,
        post_options,
        post_req,
        timeout;

    body[Constants.JSON_REGISTRATION_IDS] = registrationIds;

    if (message.delayWhileIdle !== undefined) {
        body[Constants.PARAM_DELAY_WHILE_IDLE] = message.delayWhileIdle;
    }
    if (message.collapseKey !== undefined) {
        body[Constants.PARAM_COLLAPSE_KEY] = message.collapseKey;
    }
    if (message.timeToLive !== undefined) {
        body[Constants.PARAM_TIME_TO_LIVE] = message.timeToLive;
    }
    if (message.hasData) {
        body[Constants.PARAM_PAYLOAD_KEY] = message.data;
    }

    requestBody = JSON.stringify(body);

    post_options = {
        method: 'POST',
        headers: {
            'Content-Type': 'application/json',
            'Content-length': Buffer.byteLength(requestBody, 'utf8'),
            'Authorization': 'key=' + this.key
        },
        uri: Constants.GCM_SEND_URI,
        body: requestBody
    };

    if(this.options && this.options.proxy){
        post_options.proxy = this.options.proxy;
    }

    timeout = Constants.SOCKET_TIMEOUT;

    if(this.options && this.options.timeout){
        timeout =  this.options.timeout;
    }

    post_options.timeout = timeout;

    post_req = req(post_options, function (err, res, resBody) {

        if (err)
            return callback(err, null);

        if (!res)
            return callback('response is null', null);

        if (res.statusCode === 503) {
            console.log('GCM service is unavailable');
            return callback(statusCode, null);
        } else if(res.statusCode == 401){
            console.log('Unauthorized');
            return callback(statusCode, null);
        } else if (res.statusCode !== 200) {
            console.log('Invalid request: ' + statusCode);
            return callback(statusCode, null);
        }

        // Make sure that we don't crash in case something goes wrong while
        // handling the response.
        try {
            var data = JSON.parse(resBody);
        } catch (e) {
            console.log("Error handling GCM response " + e);
            return callback("error", null);
        }
        callback(null, data);
    });
};

Sender.prototype.send = function (message, registrationId, retries, callback) {

    var attempt = 1,
        backoff = Constants.BACKOFF_INITIAL_DELAY;

    if (registrationId.length === 1) {

        this.sendNoRetry(message, registrationId, function lambda(err, result) {

            if (result === undefined) {
                if (attempt < retries) {
                    var sleepTime = backoff * 2 * attempt;
                    if (sleepTime > Constants.MAX_BACKOFF_DELAY) {
                        sleepTime = Constants.MAX_BACKOFF_DELAY;
                    }
                    timer.setTimeout(function () {
                        sendNoRetryMethod(message, registrationId, lambda);
                    }, sleepTime);
                } else {
                    console.log('Could not send message after ' + retries + ' attempts');
                    callback(null, result);
                }
                attempt += 1;
            } else callback(null, result);
        });
    } else if (registrationId.length > 1) {
        this.sendNoRetry(message, registrationId, function lambda(err, result) {

            if (attempt < retries) {
                var sleepTime = backoff * 2 * attempt,
                    unsentRegIds = [],
                    i;
                if (sleepTime > Constants.MAX_BACKOFF_DELAY) {
                    sleepTime = Constants.MAX_BACKOFF_DELAY;
                }

                if (result) {
                    for (i = 0; i < registrationId.length; i += 1) {
                        if (result.results[i].error === 'Unavailable') {
                            unsentRegIds.push(registrationId[i]);
                        }
                    }
                }

                registrationId = unsentRegIds;
                if (registrationId.length !== 0) {
                    timer.setTimeout(function () {
                        sendNoRetryMethod(message, registrationId, lambda);
                    }, sleepTime);
                    attempt += 1;
                } else callback(null, result);

            } else {
                console.log('Could not send message to all devices after ' + retries + ' attempts');
                callback(null, result);
            }
        });
    } else {
        console.log('No RegistrationIds given!');
    }
};

module.exports = Sender;
<commit_msg>Fixed the wrong references to the response's statusCode.<commit_after>/*!
 * node-gcm
 * Copyright(c) 2013 Marcus Farkas <toothlessgear@finitebox.com>
 * MIT Licensed
 */
var Constants = require('./constants');
var Result = require('./result');

var https = require('https');
var timer = require('timers');
var req = require('request')

function Sender(key , options) {
    this.key = key;
    this.options = options ;
}

var sendNoRetryMethod = Sender.prototype.sendNoRetry = function (message, registrationIds, callback) {
    var body = {},
        result = new Result(),
        requestBody,
        post_options,
        post_req,
        timeout;

    body[Constants.JSON_REGISTRATION_IDS] = registrationIds;

    if (message.delayWhileIdle !== undefined) {
        body[Constants.PARAM_DELAY_WHILE_IDLE] = message.delayWhileIdle;
    }
    if (message.collapseKey !== undefined) {
        body[Constants.PARAM_COLLAPSE_KEY] = message.collapseKey;
    }
    if (message.timeToLive !== undefined) {
        body[Constants.PARAM_TIME_TO_LIVE] = message.timeToLive;
    }
    if (message.hasData) {
        body[Constants.PARAM_PAYLOAD_KEY] = message.data;
    }

    requestBody = JSON.stringify(body);

    post_options = {
        method: 'POST',
        headers: {
            'Content-Type': 'application/json',
            'Content-length': Buffer.byteLength(requestBody, 'utf8'),
            'Authorization': 'key=' + this.key
        },
        uri: Constants.GCM_SEND_URI,
        body: requestBody
    };

    if(this.options && this.options.proxy){
        post_options.proxy = this.options.proxy;
    }

    timeout = Constants.SOCKET_TIMEOUT;

    if(this.options && this.options.timeout){
        timeout =  this.options.timeout;
    }

    post_options.timeout = timeout;

    post_req = req(post_options, function (err, res, resBody) {

        if (err)
            return callback(err, null);

        if (!res)
            return callback('response is null', null);

        if (res.statusCode === 503) {
            console.log('GCM service is unavailable');
            return callback(res.statusCode, null);
        } else if(res.statusCode == 401){
            console.log('Unauthorized');
            return callback(res.statusCode, null);
        } else if (res.statusCode !== 200) {
            console.log('Invalid request: ' + statusCode);
            return callback(res.statusCode, null);
        }

        // Make sure that we don't crash in case something goes wrong while
        // handling the response.
        try {
            var data = JSON.parse(resBody);
        } catch (e) {
            console.log("Error handling GCM response " + e);
            return callback("error", null);
        }
        callback(null, data);
    });
};

Sender.prototype.send = function (message, registrationId, retries, callback) {

    var attempt = 1,
        backoff = Constants.BACKOFF_INITIAL_DELAY;

    if (registrationId.length === 1) {

        this.sendNoRetry(message, registrationId, function lambda(err, result) {

            if (result === undefined) {
                if (attempt < retries) {
                    var sleepTime = backoff * 2 * attempt;
                    if (sleepTime > Constants.MAX_BACKOFF_DELAY) {
                        sleepTime = Constants.MAX_BACKOFF_DELAY;
                    }
                    timer.setTimeout(function () {
                        sendNoRetryMethod(message, registrationId, lambda);
                    }, sleepTime);
                } else {
                    console.log('Could not send message after ' + retries + ' attempts');
                    callback(null, result);
                }
                attempt += 1;
            } else callback(null, result);
        });
    } else if (registrationId.length > 1) {
        this.sendNoRetry(message, registrationId, function lambda(err, result) {

            if (attempt < retries) {
                var sleepTime = backoff * 2 * attempt,
                    unsentRegIds = [],
                    i;
                if (sleepTime > Constants.MAX_BACKOFF_DELAY) {
                    sleepTime = Constants.MAX_BACKOFF_DELAY;
                }

                if (result) {
                    for (i = 0; i < registrationId.length; i += 1) {
                        if (result.results[i].error === 'Unavailable') {
                            unsentRegIds.push(registrationId[i]);
                        }
                    }
                }

                registrationId = unsentRegIds;
                if (registrationId.length !== 0) {
                    timer.setTimeout(function () {
                        sendNoRetryMethod(message, registrationId, lambda);
                    }, sleepTime);
                    attempt += 1;
                } else callback(null, result);

            } else {
                console.log('Could not send message to all devices after ' + retries + ' attempts');
                callback(null, result);
            }
        });
    } else {
        console.log('No RegistrationIds given!');
    }
};

module.exports = Sender;
<|endoftext|>


<commit_before>// Copyright (C) 2013 - Texas Instruments, Jason Kridner
// Modified by Aditya Patadia, Octal Consulting LLP
var fs = require('fs');
var pinmap = require('./pinmap');
var bone = require('./bone');
var serialPort = bone.require('serialport');

var serial = {
	ports: pinmap.uarts,
	openPorts: {}
};

module.exports = {

	enable : function(path, callback) {
		debug("serial.open(" + path + ")");
		var txPin = serial.ports[path].tx;
		var rxPin = serial.ports[path].rx;
		var setTxPinState = false;

		bone.find_sysfsFile(txPin + "_pinmux", bone.is_ocp(), p + '.', onFindPinmux);

		function onFindPinmux(err, data) {
			if (err) {
				err = new verror(err, "Error finding pinmux for: " + txPin);
				console.error(err.message);
				if (callback) callback(err);
				return;
			}
			fs.writeFile(data.path + "/state", "uart", onWriteState);
		}

		function onWriteState(err) {
			if (err) {
				err = new verror(err, "Error setting pin state to UART. Please unload UART cape if already loaded.");
				console.error(err.message);
				if (callback) callback(err);
				return;
			}
			if (setTxPinState) {
				if(callback) callback(null)
			} else {
				setTxPinState = true;
				bone.find_sysfsFile(rxPin + "_pinmux", bone.is_ocp(), p + '.', onFindPinmux);
			}
		}
	},

	open: function(path, options, handler, callback) {
		debug("serial.open(" + path + ", " + options + ")");

		module.exports.enable(path, onSerialEnable);

		function onSerialEnable() {
			if (!serial.openPorts[path]) {
				var port = new serialPort(path, options);
				port.on("open", function(err) {
					if (err) {
						err = new verror(err, "Can't open port : " + path);
						console.error(err.message);
						if (callback) callback(err, null);
						return;
					}
					debug('opened serial port: ' + path);
					port.on('data', handler);
					serial.openPorts[path] = port;
					if (callback) callback(null, serial.openPorts[path]);
				});
			} else {
				if (callback) callback(null, serial.openPorts[path]);
			}
		}

	},

	parsers: serialPort.exists ? serialPort.parsers : {}
};<commit_msg>error handling improved in serial port<commit_after>// Copyright (C) 2013 - Texas Instruments, Jason Kridner
// Modified by Aditya Patadia, Octal Consulting LLP
var fs = require('fs');
var pinmap = require('./pinmap');
var bone = require('./bone');
var serialPort = bone.require('serialport');

var serial = {
	ports: pinmap.uarts,
	openPorts: {}
};

module.exports = {

	enable : function(path, callback) {
		debug("serial.open(" + path + ")");
		if(!serial.ports[path]){
			throw new verror("Supplied path:" + path + " is not a vaild serial port path.")
		}
		var txPin = serial.ports[path].tx;
		var rxPin = serial.ports[path].rx;
		var setTxPinState = false;

		bone.find_sysfsFile(txPin + "_pinmux", bone.is_ocp(), p + '.', onFindPinmux);

		function onFindPinmux(err, data) {
			if (err) {
				err = new verror(err, "Error finding pinmux for: " + txPin);
				console.error(err.message);
				if (callback) callback(err);
				return;
			}
			fs.writeFile(data.path + "/state", "uart", onWriteState);
		}

		function onWriteState(err) {
			if (err) {
				err = new verror(err, "Error setting pin state to UART. Please unload UART cape if already loaded.");
				console.error(err.message);
				if (callback) callback(err);
				return;
			}
			if (setTxPinState) {
				if(callback) callback(null)
			} else {
				setTxPinState = true;
				bone.find_sysfsFile(rxPin + "_pinmux", bone.is_ocp(), p + '.', onFindPinmux);
			}
		}
	},

	open: function(path, options, handler, callback) {
		debug("serial.open(" + path + ", " + options + ")");

		if(!serial.ports[path]){
			throw new verror("Supplied path:" + path + " is not a vaild serial port path.")
		}

		if(typeof handler != "function" || typeof callback != 'function'){
			throw new verror("handler and callback must be provided to serial.open and both should be functions");
		}

		module.exports.enable(path, onSerialEnable);

		function onSerialEnable() {
			if (!serial.openPorts[path]) {
				var port = new serialPort(path, options);
				port.on("open", function(err) {
					if (err) {
						err = new verror(err, "Can't open port : " + path);
						console.error(err.message);
						if (callback) callback(err, null);
						return;
					}
					debug('opened serial port: ' + path);
					port.on('data', handler);
					serial.openPorts[path] = port;
					if (callback) callback(null, serial.openPorts[path]);
				});
			} else {
				if (callback) callback(null, serial.openPorts[path]);
			}
		}

	},

	parsers: serialPort.exists ? serialPort.parsers : {}
};<|endoftext|>


<commit_before>const Crypto = require('crypto');
const Extensions = require('websocket-extensions');
const URL = require('url');

const HTTPStatusCodes = require('../resources/HTTPStatusCodes.json');
const WebSocketBase = require('./base.js');
const WS13 = require('./index.js');

const HTTP_VERSION = 1.1;
const WEBSOCKET_VERSION = 13;

WS13.WebSocketServer = WebSocketServer;
require('util').inherits(WebSocketServer, require('events').EventEmitter);

function WebSocketServer(options) {
	this.options = {
		"pingInterval": 10000,
		"pingTimeout": 10000,
		"pingFailures": 3
	};

	options = options || {};
	for (let i in options) {
		if (options.hasOwnProperty(i)) {
			this.options[i] = options[i];
		}
	}

	this.protocols = this.options.protocols || [];
}

WebSocketServer.prototype.http = function(server) {
	server.on('upgrade', (req, socket, head) => {
		if (!req.headers.upgrade || req.headers.upgrade.toLowerCase() != "websocket") {
			bail("Invalid upgrade type. Supported: websocket");
			return;
		}

		if (!req.headers.connection || req.headers.connection.toLowerCase().split(',').map(item => item.trim()).indexOf("upgrade") == -1) {
			bail("Invalid upgrade request.");
			return;
		}

		let httpV = req.httpVersion.split('.');
		if (httpV[0] < 1 || httpV[1] < 1) {
			bail("Invalid HTTP version for websocket upgrade.");
			return;
		}

		if (req.method.toUpperCase() != 'GET') {
			bail("Bad HTTP method. Required: GET");
			return;
		}

		if (!req.headers['sec-websocket-key'] || Buffer.from(req.headers['sec-websocket-key'], 'base64').length != 16) {
			bail("Missing or invalid Sec-WebSocket-Key.");
			return;
		}

		if (req.headers['sec-websocket-version'] != WEBSOCKET_VERSION) {
			bail("Sec-WebSocket-Version must be " + WEBSOCKET_VERSION + ".");
			return;
		}

		if (!socket.remoteAddress) {
			bail("Unable to determine IP address.");
			return;
		}

		let selectedProtocol = null;
		let protocols = [];
		if (req.headers['sec-websocket-protocol']) {
			protocols = req.headers['sec-websocket-protocol'].split(',').map(protocol => protocol.trim());
			// Do any of these match?

			for (let i = 0; i < protocols.length; i++) {
				if (this.protocols.indexOf(protocols[i]) != -1) {
					selectedProtocol = protocols[i];
					break;
				}
			}
		}

		let uri = URL.parse(req.url, true);

		let extensions = new Extensions();
		extensions.add(require('permessage-deflate'));
		let selectedExtensions = extensions.generateResponse(req.headers['sec-websocket-extensions']);

		let handshakeData = {
			"path": uri.pathname,
			"query": uri.query,
			"headers": req.headers,
			"httpVersion": req.httpVersion,
			"origin": req.headers.origin || null,
			"extensions": req.headers['sec-websocket-extensions'],
			"extensionsHandler": extensions,
			"selectedExtensions": selectedExtensions,
			"protocols": protocols || [],
			"selectedProtocol": selectedProtocol || null,
			"auth": null,
			"cookies": {},
			"remoteAddress": socket.remoteAddress.replace(/^::ffff:/, ''),
			"socket": socket
		};

		// Does it have HTTP authorization?
		if (req.headers.authorization) {
			let match = req.headers.authorization.match(/basic ((?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=|[A-Za-z0-9+/]{4}))/i);
			if (match) {
				handshakeData.auth = Buffer.from(match[1], 'base64').toString('utf8');
			}
		}

		// Does it have cookies?
		if (req.headers.cookie) {
			req.headers.cookie.split(';').map(cookie => cookie.trim().split('=')).forEach(cookie => {
				handshakeData.cookies[cookie[0].trim()] = decodeURIComponent(cookie.slice(1).join('=').trim());
			});
		}

		// Everything looks okay so far, make sure we'd like to accept this.
		this.emit('handshake', handshakeData, (statusCode, body, headers) => {
			// REJECT
			req.statusCode = statusCode || 403;
			headers = headers || {};
			socket.end(buildResponse(statusCode || 403, headers, body));
		}, (response) => {
			// ACCEPT
			response = response || {};
			let headers = response.headers || {};

			let options = {
				"pingInterval": this.options.pingInterval,
				"pingTimeout": this.options.pingTimeout,
				"pingFailures": this.options.pingFailures,
				"extensions": extensions
			};

			headers.Upgrade = "websocket";
			headers.Connection = "Upgrade";
			headers['Sec-WebSocket-Accept'] = Crypto.createHash('sha1').update(req.headers['sec-websocket-key'] + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11").digest('base64');

			// Check if the accept method overrode our selected subprotocol
			if (typeof response.protocol !== 'undefined') {
				handshakeData.selectedProtocol = response.protocol || null;
			}

			if (response.extensions) {
				options.extensions = extensions = response.extensions;
				selectedExtensions = extensions.generateResponse(req.headers['sec-websocket-extensions']);
			}

			if (selectedExtensions) {
				headers['Sec-WebSocket-Extensions'] = selectedExtensions;
			}

			if (handshakeData.selectedProtocol) {
				headers['Sec-WebSocket-Protocol'] = handshakeData.selectedProtocol;
			}

			socket.write(buildResponse(101, headers));

			response.options = response.options || {};
			for (let i in response.options) {
				if (response.options.hasOwnProperty(i)) {
					options[i] = response.options[i];
				}
			}

			let websocket = new WebSocket(socket, options, handshakeData, head);
			this.emit('connection', websocket);
			return websocket;
		});

		function bail(err) {
			if (server.listenerCount('upgrade') != 1) {
				// Something else could pick this up
				return;
			}

			socket.end(buildResponse(400, null, err));
		}
	});
};

function buildResponse(code, headers, body) {
	let response = "HTTP/" + HTTP_VERSION + " " + code + " " + (HTTPStatusCodes[code] || "Unknown Response") + "\r\n";

	headers = headers || {};
	headers.Server = "node-websocket13/" + require('../package.json').version;
	headers.Date = new Date().toUTCString();

	if (typeof body === 'object') {
		body = JSON.stringify(body);
		headers['Content-Type'] = 'application/json';
	}

	if (body) {
		headers['Content-Length'] = Buffer.byteLength(body);
	} else if (code != 204 && code != 101) {
		headers['Content-Length'] = 0;
	}

	for (let i in headers) {
		if (headers.hasOwnProperty(i)) {
			response += i + ": " + headers[i] + "\r\n";
		}
	}

	response += "\r\n" + (typeof body !== 'undefined' ? body : '');
	return response;
}

// Server-spawned WebSocket object
require('util').inherits(WebSocket, WebSocketBase);

function WebSocket(socket, options, handshakeData, head) {
	WebSocketBase.call(this);

	options = options || {};
	for (let i in options) {
		if (options.hasOwnProperty(i)) {
			this.options[i] = options[i];
		}
	}

	this.state = WS13.State.Connected;
	this.handshakeData = handshakeData;
	this.extensions = options.extensions;
	this.protocol = handshakeData.selectedProtocol || null;

	this._socket = socket;
	this._type = 'server';

	this._prepSocketEvents();
	if (head && head.length > 0) {
		this._dataBuffer = head; // don't call _handleData just yet, as there are no event listeners bound
	}

	this.emit('connected'); // perform connect tasks
}
<commit_msg>Added permessageDeflate server option<commit_after>const Crypto = require('crypto');
const Extensions = require('websocket-extensions');
const URL = require('url');

const HTTPStatusCodes = require('../resources/HTTPStatusCodes.json');
const WebSocketBase = require('./base.js');
const WS13 = require('./index.js');

const HTTP_VERSION = 1.1;
const WEBSOCKET_VERSION = 13;

WS13.WebSocketServer = WebSocketServer;
require('util').inherits(WebSocketServer, require('events').EventEmitter);

function WebSocketServer(options) {
	this.options = {
		pingInterval: 10000,
		pingTimeout: 10000,
		pingFailures: 3,
		permessageDeflate: true
	};

	options = options || {};
	for (let i in options) {
		if (options.hasOwnProperty(i)) {
			this.options[i] = options[i];
		}
	}

	this.protocols = this.options.protocols || [];
}

WebSocketServer.prototype.http = function(server) {
	server.on('upgrade', (req, socket, head) => {
		if (!req.headers.upgrade || req.headers.upgrade.toLowerCase() != "websocket") {
			bail("Invalid upgrade type. Supported: websocket");
			return;
		}

		if (!req.headers.connection || req.headers.connection.toLowerCase().split(',').map(item => item.trim()).indexOf("upgrade") == -1) {
			bail("Invalid upgrade request.");
			return;
		}

		let httpV = req.httpVersion.split('.');
		if (httpV[0] < 1 || httpV[1] < 1) {
			bail("Invalid HTTP version for websocket upgrade.");
			return;
		}

		if (req.method.toUpperCase() != 'GET') {
			bail("Bad HTTP method. Required: GET");
			return;
		}

		if (!req.headers['sec-websocket-key'] || Buffer.from(req.headers['sec-websocket-key'], 'base64').length != 16) {
			bail("Missing or invalid Sec-WebSocket-Key.");
			return;
		}

		if (req.headers['sec-websocket-version'] != WEBSOCKET_VERSION) {
			bail("Sec-WebSocket-Version must be " + WEBSOCKET_VERSION + ".");
			return;
		}

		if (!socket.remoteAddress) {
			bail("Unable to determine IP address.");
			return;
		}

		let selectedProtocol = null;
		let protocols = [];
		if (req.headers['sec-websocket-protocol']) {
			protocols = req.headers['sec-websocket-protocol'].split(',').map(protocol => protocol.trim());
			// Do any of these match?

			for (let i = 0; i < protocols.length; i++) {
				if (this.protocols.indexOf(protocols[i]) != -1) {
					selectedProtocol = protocols[i];
					break;
				}
			}
		}

		let uri = URL.parse(req.url, true);

		let extensions = new Extensions();
		if (this.options.permessageDeflate) {
			extensions.add(require('permessage-deflate'));
		}
		let selectedExtensions = extensions.generateResponse(req.headers['sec-websocket-extensions']);

		let handshakeData = {
			"path": uri.pathname,
			"query": uri.query,
			"headers": req.headers,
			"httpVersion": req.httpVersion,
			"origin": req.headers.origin || null,
			"extensions": req.headers['sec-websocket-extensions'],
			"extensionsHandler": extensions,
			"selectedExtensions": selectedExtensions,
			"protocols": protocols || [],
			"selectedProtocol": selectedProtocol || null,
			"auth": null,
			"cookies": {},
			"remoteAddress": socket.remoteAddress.replace(/^::ffff:/, ''),
			"socket": socket
		};

		// Does it have HTTP authorization?
		if (req.headers.authorization) {
			let match = req.headers.authorization.match(/basic ((?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=|[A-Za-z0-9+/]{4}))/i);
			if (match) {
				handshakeData.auth = Buffer.from(match[1], 'base64').toString('utf8');
			}
		}

		// Does it have cookies?
		if (req.headers.cookie) {
			req.headers.cookie.split(';').map(cookie => cookie.trim().split('=')).forEach(cookie => {
				handshakeData.cookies[cookie[0].trim()] = decodeURIComponent(cookie.slice(1).join('=').trim());
			});
		}

		// Everything looks okay so far, make sure we'd like to accept this.
		this.emit('handshake', handshakeData, (statusCode, body, headers) => {
			// REJECT
			req.statusCode = statusCode || 403;
			headers = headers || {};
			socket.end(buildResponse(statusCode || 403, headers, body));
		}, (response) => {
			// ACCEPT
			response = response || {};
			let headers = response.headers || {};

			let options = {
				"pingInterval": this.options.pingInterval,
				"pingTimeout": this.options.pingTimeout,
				"pingFailures": this.options.pingFailures,
				"extensions": extensions
			};

			headers.Upgrade = "websocket";
			headers.Connection = "Upgrade";
			headers['Sec-WebSocket-Accept'] = Crypto.createHash('sha1').update(req.headers['sec-websocket-key'] + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11").digest('base64');

			// Check if the accept method overrode our selected subprotocol
			if (typeof response.protocol !== 'undefined') {
				handshakeData.selectedProtocol = response.protocol || null;
			}

			if (response.extensions) {
				options.extensions = extensions = response.extensions;
				selectedExtensions = extensions.generateResponse(req.headers['sec-websocket-extensions']);
			}

			if (selectedExtensions) {
				headers['Sec-WebSocket-Extensions'] = selectedExtensions;
			}

			if (handshakeData.selectedProtocol) {
				headers['Sec-WebSocket-Protocol'] = handshakeData.selectedProtocol;
			}

			socket.write(buildResponse(101, headers));

			response.options = response.options || {};
			for (let i in response.options) {
				if (response.options.hasOwnProperty(i)) {
					options[i] = response.options[i];
				}
			}

			let websocket = new WebSocket(socket, options, handshakeData, head);
			this.emit('connection', websocket);
			return websocket;
		});

		function bail(err) {
			if (server.listenerCount('upgrade') != 1) {
				// Something else could pick this up
				return;
			}

			socket.end(buildResponse(400, null, err));
		}
	});
};

function buildResponse(code, headers, body) {
	let response = "HTTP/" + HTTP_VERSION + " " + code + " " + (HTTPStatusCodes[code] || "Unknown Response") + "\r\n";

	headers = headers || {};
	headers.Server = "node-websocket13/" + require('../package.json').version;
	headers.Date = new Date().toUTCString();

	if (typeof body === 'object') {
		body = JSON.stringify(body);
		headers['Content-Type'] = 'application/json';
	}

	if (body) {
		headers['Content-Length'] = Buffer.byteLength(body);
	} else if (code != 204 && code != 101) {
		headers['Content-Length'] = 0;
	}

	for (let i in headers) {
		if (headers.hasOwnProperty(i)) {
			response += i + ": " + headers[i] + "\r\n";
		}
	}

	response += "\r\n" + (typeof body !== 'undefined' ? body : '');
	return response;
}

// Server-spawned WebSocket object
require('util').inherits(WebSocket, WebSocketBase);

function WebSocket(socket, options, handshakeData, head) {
	WebSocketBase.call(this);

	options = options || {};
	for (let i in options) {
		if (options.hasOwnProperty(i)) {
			this.options[i] = options[i];
		}
	}

	this.state = WS13.State.Connected;
	this.handshakeData = handshakeData;
	this.extensions = options.extensions;
	this.protocol = handshakeData.selectedProtocol || null;

	this._socket = socket;
	this._type = 'server';

	this._prepSocketEvents();
	if (head && head.length > 0) {
		this._dataBuffer = head; // don't call _handleData just yet, as there are no event listeners bound
	}

	this.emit('connected'); // perform connect tasks
}
<|endoftext|>


<commit_before>var debug = require('debug')('webtorrent:server')
var http = require('http')
var mime = require('mime')
var pump = require('pump')
var rangeParser = require('range-parser')
var url = require('url')

module.exports = function Server (client, port) {
  var server = http.createServer()

  server.on('connection', function (socket) {
    socket.setTimeout(36000000)
  })

  server.on('request', function (req, res) {
    debug('onRequest')

    // Allow CORS requests to specify arbitrary headers, e.g. 'Range',
    // by responding to the OPTIONS preflight request with the specified
    // origin and requested headers.
    if (req.method === 'OPTIONS' && req.headers['access-control-request-headers']) {
      res.setHeader('Access-Control-Allow-Origin', req.headers.origin)
      res.setHeader('Access-Control-Allow-Methods', 'POST, GET, OPTIONS')
      res.setHeader(
          'Access-Control-Allow-Headers',
          req.headers['access-control-request-headers'])
      res.setHeader('Access-Control-Max-Age', '1728000')
      return res.end()
    }

    if (req.headers.origin)
      res.setHeader('Access-Control-Allow-Origin', req.headers.origin)

    var pathname = url.parse(req.url).pathname
    if (pathname === '/favicon.ico') return res.end()
    if (pathname === '/') pathname = '/' + client.index

    var index = Number(pathname.slice(1))
    if (Number.isNaN(index) || index >= client.torrent.files.length) {
      res.statusCode = 404
      return res.end()
    }

    if (client.torrent) onTorrent(client.torrent)
    else client.once('torrent', onTorrent)

    function onTorrent (torrent) {
      var file = torrent.files[index]

      res.setHeader('Accept-Ranges', 'bytes')
      res.setHeader('Content-Type', mime.lookup(file.name))
      res.statusCode = 206

      var range
      if (req.headers.range) {
        // no support for multi-range reqs
        range = rangeParser(file.length, req.headers.range)[0]
        debug('range %s', JSON.stringify(range))
        res.setHeader(
          'Content-Range',
          'bytes ' + range.start + '-' + range.end + '/' + file.length
        )
        res.setHeader('Content-Length', range.end - range.start + 1)
      } else {
        res.setHeader('Content-Length', file.length)
      }
      if (req.method === 'HEAD') res.end()
      pump(file.createReadStream(range), res)
    }
  })
  server.listen(port)

  return server
}
<commit_msg>server: remove duplicate Access-Control-Allow-Origin header<commit_after>var debug = require('debug')('webtorrent:server')
var http = require('http')
var mime = require('mime')
var pump = require('pump')
var rangeParser = require('range-parser')
var url = require('url')

module.exports = function Server (client, port) {
  var server = http.createServer()

  server.on('connection', function (socket) {
    socket.setTimeout(36000000)
  })

  server.on('request', function (req, res) {
    debug('onRequest')

    // Allow CORS requests to specify arbitrary headers, e.g. 'Range',
    // by responding to the OPTIONS preflight request with the specified
    // origin and requested headers.
    if (req.method === 'OPTIONS' && req.headers['access-control-request-headers']) {
      res.setHeader('Access-Control-Allow-Methods', 'POST, GET, OPTIONS')
      res.setHeader(
          'Access-Control-Allow-Headers',
          req.headers['access-control-request-headers']
      )
      res.setHeader('Access-Control-Max-Age', '1728000')
      return res.end()
    }

    if (req.headers.origin)
      res.setHeader('Access-Control-Allow-Origin', req.headers.origin)

    var pathname = url.parse(req.url).pathname
    if (pathname === '/favicon.ico') return res.end()
    if (pathname === '/') pathname = '/' + client.index

    var index = Number(pathname.slice(1))
    if (Number.isNaN(index) || index >= client.torrent.files.length) {
      res.statusCode = 404
      return res.end()
    }

    if (client.torrent) onTorrent(client.torrent)
    else client.once('torrent', onTorrent)

    function onTorrent (torrent) {
      var file = torrent.files[index]

      res.setHeader('Accept-Ranges', 'bytes')
      res.setHeader('Content-Type', mime.lookup(file.name))
      res.statusCode = 206

      var range
      if (req.headers.range) {
        // no support for multi-range reqs
        range = rangeParser(file.length, req.headers.range)[0]
        debug('range %s', JSON.stringify(range))
        res.setHeader(
          'Content-Range',
          'bytes ' + range.start + '-' + range.end + '/' + file.length
        )
        res.setHeader('Content-Length', range.end - range.start + 1)
      } else {
        res.setHeader('Content-Length', file.length)
      }
      if (req.method === 'HEAD') res.end()
      pump(file.createReadStream(range), res)
    }
  })
  server.listen(port)

  return server
}
<|endoftext|>


<commit_before>
/**
 * Module dependencies.
 */

var _ = require('lodash')
  , intercept = require('./intercept')
  , Session = require('./session')
  , Firewall = require('./firewall')
  ;

/**
 * VantageServer prototype.
 */

var vantageServer = VantageServer.prototype;

/**
 * Expose `VantageServer`.
 */

exports = module.exports = VantageServer;

/**
 * Initialize a new `VantageServer` instance.
 *
 * @param {Vantage} parent
 * @return {VantageServer}
 * @api public
 */

function VantageServer(parent) {
  if (!(this instanceof VantageServer)) return new VantageServer;
  this._hooked = false;

  // Sessions are created when you use
  // vantage externally to remotely connect
  // to this running instance of vantage.
  // Every connection (a socket.io connection)
  // is stored as a Session object.
  this.sessions = [];

  this.parent = parent;
  return this;
};

/**
 * Gets vantage started as a server.
 *
 * @param {App} app
 * @param {Object} options
 * @return {VantageServer}
 * @api public
 */

vantageServer.init = function(app, options, cb) {
  var self = this;

  // If options is a function, we think it's
  // a callback.
  cb = (_.isFunction(options)) ? options
    : (cb || function(){});

  // If a port is passed in instead of options,
  // accept that by converting it to a filled object.
  // Otherwise, default to an empty object.
  options = (options && _.isFunction(options)) ? {}
    : (options && !_.isObject(options) && !isNaN(options)) ? ({ port: options })
    : (!options) ? {}
    : options;

  // If a port was passed as the 'app', assign it
  // as the port instead.
  options.port =
    (app && !_.isObject(app) && !isNaN(app)) ? app : options.port;

  // If only a port was passed as the app, make the app
  // object a blank function.
  app =
    (app && !_.isObject(app) && !isNaN(app)) ? (function(){}) : app;

  var appIs =
    (_.isFunction(app)) ? 'callback' :
    (_.isObject(app) && _.isFunction(app.callback)) ? 'koa' :
    '';

  options = _.defaults(options, {
    port: 80,
    ssl: false,
    logActivity: false
  });

  var appCallback =
    (appIs == 'callback') ? app :
    (appIs == 'koa') ? app.callback() :
    void 0;

  if (!appCallback) {
    throw new Error('Unsupported HTTP Server passed into Vantage.'); return;
  }

  var type = (options.ssl) ? 'https' : 'http';
  if (type == 'http') {
    this.server = require(type).createServer(appCallback);
  } else {
    this.server = require(type).createServer(options, appCallback);
  }

  this.io = require('socket.io')(this.server);
  this.server.listen(options.port);
  this._port = options.port;
  this._logActivity = options.logActivity;

  this.io.set('authorization', function(handshakeData, accept){

    var address = handshakeData.connection._peername;
    var valid = self.firewall.valid(address);
    var query = handshakeData._query;
    var id = query.id || void 0;

    // If Vantage has the same unique ID, don't let
    // it connect to itself.
    if (id && parseFloat(id) === parseFloat(self.parent._id)) {
      return accept("You can't connect to yourself.", false);
    }

    if (!valid) {
      return accept('IP Not Allowed: ' + address, false);
    } else {
      return accept(void 0, true);
    }

  });

  this.firewall = new Firewall();
  this.parent.firewall = this.firewall;

  this.listen(cb);

  return this;
};

/**
 * Creates server socket connections and
 * registers all events.
 *
 * @return {VantageServer}
 * @api private
 */

vantageServer.listen = function(cb) {
  var self =  this;
  cb = cb || function(){}

  this.io.on('connection', function(socket) {

    var session = new Session();
    session.io = socket;
    self.sessions.push(session);
    self.parent.is('server', true);

    session.io.on('vantage-keypress-upstream', function(data) {
      self.parent._proxy('vantage-keypress-upstream', 'upstream', data).then(function(){
        if ((['up', 'down', 'tab'].indexOf(data.key) > -1)) {
          var response = self.parent._getKeypressResult(data.key, data.value);
          self.parent._send('vantage-keypress-downstream', 'downstream', { value: response });
        } else {
          self.parent._histCtr = 0;
        }
      });
    });

    session.io.on('vantage-command-upstream', function(data) {
      self.parent._proxy('vantage-command-upstream', 'upstream', data).then(function() {
        if (data.command) {

          var execute = function() {
            return new Promise(function(resolve, reject){
              var cmd = {
                command: data.command,
                resolve: resolve,
                reject: reject,
                callback: function(resp) {
                  self.parent.emit('server_command_executed', {
                    command: data.command,
                    response: resp
                  });
                  self.parent._send('vantage-command-downstream', 'downstream', { command: data.command, completed: true, error: void 0, data: resp });
                },
              }
              self.parent._exec(cmd);
            });
          }

          self.parent.emit('server_command_received', {
            command: data.command
          });

          execute().then(function(resp) {
            self.parent.emit('server_command_executed', {
              command: data.command,
              response: resp
            });
            self.parent._send('vantage-command-downstream', 'downstream', { command: data.command, completed: true, error: void 0, data: resp });
          }).catch(function(error){
            self.parent.emit('server_command_error', {
              command: data.command,
              error: error
            });
            self.parent._send('vantage-command-downstream', 'downstream', { command: data.command, completed: true, error: error, data: void 0 });
          });
        }
      });
    });

    session.io.on('vantage-heartbeat-upstream', function(data) {
      self.parent._proxy('vantage-heartbeat-upstream', 'upstream', data).then(function() {
        self.parent._send('vantage-heartbeat-downstream', 'downstream', {
          delimiter: self.parent._delimiter,
        });
      });
    });

    // Upstream > Proxy > Downstream (Prompt User) > @Proxy > @Upstream (Use Data).
    session.io.on('vantage-prompt-upstream', function(data) {
      self.parent._proxy('vantage-prompt-upstream', 'upstream', data).then(function() {
        self.parent.emit('vantage-prompt-upstream', data);
      });
    });

    session.io.on('disconnect', function(data) {

      self.parent.emit('server_disconnect', data);

      var nw = [];

      for (var i = 0; i < self.sessions.length; ++i) {
        if (self.sessions[i].io.id == session.io.id) {
          delete self.sessions[i];
        } else {
          nw.push(self.sessions[i]);
        }
      }

      self.sessions = nw;
      self.unhook();

      if (self._logActivity) {
        self.parent.log('User exited session.');
      }

      if (self.sessions.length < 1) {
        self.parent.is('server', false);
      }
    });

    if (self._logActivity) {
      self.parent.log('\nUser entering session.')
    }

    self.hook(function(txt) {
      self.parent._send('vantage-stdout-downstream', 'downstream', { value: txt });
      return txt = '';
    });

    if (self.parent._banner) {
      session.io.emit('vantage-banner-downstream', {
        banner: self.parent._banner,
      });
    }

    session.io.emit('vantage-heartbeat-downstream', {
      delimiter: self.parent._delimiter,
    });

    self.parent.emit('server_connection', socket);
    cb(socket);

  });

  return this;
};

/**
 * Unhooks stdout capture.
 *
 * @return {VantageServer}
 * @api public
 */

vantageServer.unhook = function() {
  if (this._hooked && this._unhook !== undefined && this.sessions.length < 1) {
    this._unhook();
    this._hooked = false;
    if (this._logActivity) {
      this.parent.log('Stdout returned to console.');
    }
  }
  return this;
},

/**
 * Hooks all stdout through a given function.
 *
 * @param {Function} fn
 * @return {VantageServer}
 * @api public
 */

vantageServer.hook = function(fn) {
  if (this._hooked && this._unhook != undefined) {
    this.unhook();
  }
  if (this._logActivity) {
    this.parent.log('Piping stdout downstream.');
  }
  this._unhook = intercept(fn);
  this._hooked = true;
  return this;
};
<commit_msg>small fix<commit_after>
/**
 * Module dependencies.
 */

var _ = require('lodash')
  , intercept = require('./intercept')
  , Session = require('./session')
  , Firewall = require('./firewall')
  ;

/**
 * VantageServer prototype.
 */

var vantageServer = VantageServer.prototype;

/**
 * Expose `VantageServer`.
 */

exports = module.exports = VantageServer;

/**
 * Initialize a new `VantageServer` instance.
 *
 * @param {Vantage} parent
 * @return {VantageServer}
 * @api public
 */

function VantageServer(parent) {
  if (!(this instanceof VantageServer)) return new VantageServer;
  this._hooked = false;

  // Sessions are created when you use
  // vantage externally to remotely connect
  // to this running instance of vantage.
  // Every connection (a socket.io connection)
  // is stored as a Session object.
  this.sessions = [];

  this.parent = parent;
  return this;
};

/**
 * Gets vantage started as a server.
 *
 * @param {App} app
 * @param {Object} options
 * @return {VantageServer}
 * @api public
 */

vantageServer.init = function(app, options, cb) {
  var self = this;

  // If options is a function, we think it's
  // a callback.
  cb = (_.isFunction(options)) ? options
    : (cb || function(){});

  // If a port is passed in instead of options,
  // accept that by converting it to a filled object.
  // Otherwise, default to an empty object.
  options = (options && _.isFunction(options)) ? {}
    : (options && !_.isObject(options) && !isNaN(options)) ? ({ port: options })
    : (!options) ? {}
    : options;

  // If a port was passed as the 'app', assign it
  // as the port instead.
  options.port =
    (app && !_.isObject(app) && !isNaN(app)) ? app : options.port;

  // If only a port was passed as the app, make the app
  // object a blank function.
  app =
    (app && !_.isObject(app) && !isNaN(app)) ? (function(){}) : app;

  var appIs =
    (_.isFunction(app)) ? 'callback' :
    (_.isObject(app) && _.isFunction(app.callback)) ? 'koa' :
    '';

  options = _.defaults(options, {
    port: 80,
    ssl: false,
    logActivity: false
  });

  var appCallback =
    (appIs == 'callback') ? app :
    (appIs == 'koa') ? app.callback() :
    void 0;

  if (!appCallback) {
    throw new Error('Unsupported HTTP Server passed into Vantage.'); return;
  }

  var type = (options.ssl) ? 'https' : 'http';
  if (type == 'http') {
    this.server = require(type).createServer(appCallback);
  } else {
    this.server = require(type).createServer(options, appCallback);
  }

  this.io = require('socket.io')(this.server);
  this.server.listen(options.port);
  this._port = options.port;
  this._logActivity = options.logActivity;

  this.io.set('authorization', function(handshakeData, accept){

    var address = handshakeData.connection._peername;
    var valid = self.firewall.valid(address);
    var query = handshakeData._query;
    var id = query.id || void 0;

    // If Vantage has the same unique ID, don't let
    // it connect to itself.
    if (id && parseFloat(id) === parseFloat(self.parent._id)) {
      return accept("You can't connect to yourself.", false);
    }

    if (!valid) {
      return accept('IP Not Allowed: ' + address, false);
    } else {
      return accept(void 0, true);
    }

  });

  this.firewall = new Firewall();
  this.parent.firewall = this.firewall;

  this.listen(cb);

  return this;
};

/**
 * Creates server socket connections and
 * registers all events.
 *
 * @return {VantageServer}
 * @api private
 */

vantageServer.listen = function(cb) {
  var self =  this;
  cb = cb || function(){}

  this.io.on('connection', function(socket) {

    var session = new Session();
    session.io = socket;
    self.sessions.push(session);
    self.parent.is('server', true);

    session.io.on('vantage-keypress-upstream', function(data) {
      self.parent._proxy('vantage-keypress-upstream', 'upstream', data).then(function(){
        if ((['up', 'down', 'tab'].indexOf(data.key) > -1)) {
          var response = self.parent._getKeypressResult(data.key, data.value);
          self.parent._send('vantage-keypress-downstream', 'downstream', { value: response });
        } else {
          self.parent._histCtr = 0;
        }
      });
    });

    session.io.on('vantage-command-upstream', function(data) {
      self.parent._proxy('vantage-command-upstream', 'upstream', data).then(function() {
        if (data.command) {

          var execute = function() {
            return new Promise(function(resolve, reject){
              var cmd = {
                command: data.command,
                resolve: resolve,
                reject: reject,
                callback: function(resp) {
                  self.parent.emit('server_command_executed', {
                    command: data.command,
                    response: resp
                  });
                  self.parent._send('vantage-command-downstream', 'downstream', { command: data.command, completed: true, error: void 0, data: resp });
                },
              }
              self.parent._exec(cmd);
            });
          }

          self.parent.emit('server_command_received', {
            command: data.command
          });

          execute().then(function(resp) {
            self.parent.emit('server_command_executed', {
              command: data.command,
              response: resp
            });
            self.parent._send('vantage-command-downstream', 'downstream', { command: data.command, completed: true, error: void 0, data: resp });
          }).catch(function(error){
            self.parent.emit('server_command_error', {
              command: data.command,
              error: error
            });
            self.parent._send('vantage-command-downstream', 'downstream', { command: data.command, completed: true, error: error, data: void 0 });
          });
        }
      });
    });

    session.io.on('vantage-heartbeat-upstream', function(data) {
      self.parent._proxy('vantage-heartbeat-upstream', 'upstream', data).then(function() {
        self.parent._send('vantage-heartbeat-downstream', 'downstream', {
          delimiter: self.parent._delimiter,
        });
      });
    });

    // Upstream > Proxy > Downstream (Prompt User) > @Proxy > @Upstream (Use Data).
    session.io.on('vantage-prompt-upstream', function(data) {
      self.parent._proxy('vantage-prompt-upstream', 'upstream', data).then(function() {
        self.parent.emit('vantage-prompt-upstream', data);
      });
    });

    session.io.on('disconnect', function(data) {

      self.parent.emit('server_disconnect', data);

      var nw = [];

      for (var i = 0; i < self.sessions.length; ++i) {
        if (self.sessions[i].io.id == session.io.id) {
          delete self.sessions[i];
        } else {
          nw.push(self.sessions[i]);
        }
      }

      self.sessions = nw;
      self.unhook();

      if (self._logActivity) {
        self.parent.log('User exited session.');
      }

      if (self.sessions.length < 1) {
        self.parent.is('server', false);
      }
    });

    if (self._logActivity) {
      self.parent.log('\nUser entering session.')
    }

    self.hook(function(txt) {
      self.parent._send('vantage-stdout-downstream', 'downstream', { value: txt });
      return txt = '';
    });

    if (self.parent._banner) {
      session.io.emit('vantage-banner-downstream', {
        banner: self.parent._banner,
      });
    }

    session.io.emit('vantage-heartbeat-downstream', {
      delimiter: self.parent._delimiter,
    });

    self.parent.emit('server_connection', socket);
    cb(socket);

  });

  return this;
};

/**
 * Unhooks stdout capture.
 *
 * @return {VantageServer}
 * @api public
 */

vantageServer.unhook = function() {
  if (this._hooked && this._unhook !== undefined && this.sessions.length < 1) {
    this._unhook();
    this._hooked = false;
    if (this._logActivity) {
      this.parent.log('Stdout returned to console.');
    }
  }
  return this;
},

/**
 * Hooks all stdout through a given function.
 *
 * @param {Function} fn
 * @return {VantageServer}
 * @api public
 */

vantageServer.hook = function(fn) {
  if (this._hooked && this._unhook != undefined) {
    this.unhook();
  }
  if (this._logActivity) {
    this.parent.log('Piping stdout downstream.');
  }
  this._unhook = intercept(fn);
  this._hooked = true;
  return this;
};
<|endoftext|>


<commit_before>import _ from 'lodash';
import path from 'path';
import Promise from 'bluebird';
import webpack from 'webpack';
import express from 'express';
import webpackDevMiddleware from 'webpack-dev-middleware';
import jade from 'jade';
import chalk from 'chalk';
import url from 'url';

const renderFileAsync = Promise.promisify(jade.renderFile);

function webpackConfig(demoSet) {
  const config = demoSet.webpackConfig;
  const entryLib = path.resolve(demoSet.packagePath, config.entry);

  config.output = {
    filename: '[name].bundle.js',
    path: '/bundle/',
    publicPath: '/bundle/'
  };

  config.entry = _.chain(demoSet.demos)
  .mapValues(demo => demo.entry)
  .value();

  delete config.externals;

  return _.merge(config, {
    resolve: {
      alias: {
        [demoSet.packageName]: path.resolve(entryLib)
      }
    }
  });
}

export function serve(demoSet, {
  port = 8080,
  hostname = 'localhost'
} = {}) {
  const config = webpackConfig(demoSet);
  const server = express();

  server.use(webpackDevMiddleware(webpack(config), {
    publicPath: '/bundle/'
  }));

  _.forEach(demoSet.demos, demo => {
    server.get(url.resolve('/', demo.relativePath), (req, res) => {
      renderFileAsync(demo.template, {
        title: demo.name,
        bundle: `/bundle/${demo.name}.bundle.js`
      }).then(html => {
        res.send(html);
      }).catch(() => {
        res.status(404).send(`template ${demo.template} not found`);
      });
    });
  });

  const httpServer = server.listen(port, hostname);
  const urlBase = `http://${hostname}:${port}`;
  const lines = [
    chalk.bold(`${chalk.green('The server is running at')} ${chalk.cyan.underline(urlBase)}`),
    `The following example pages are available:`
  ].concat(_.map(demoSet.demos, demo => {
    const urlDemo = url.resolve(`http://${hostname}:${port}/`, demo.relativePath);
    return `  ${chalk.magenta(demo.name)}: ${chalk.cyan.underline(urlDemo)}`;
  })).concat(['']);

  global.console.log(lines.join('\n'));

  return httpServer;
}
<commit_msg>Add CORS header<commit_after>import _ from 'lodash';
import path from 'path';
import Promise from 'bluebird';
import webpack from 'webpack';
import express from 'express';
import webpackDevMiddleware from 'webpack-dev-middleware';
import jade from 'jade';
import chalk from 'chalk';
import url from 'url';

const renderFileAsync = Promise.promisify(jade.renderFile);

function webpackConfig(demoSet) {
  const config = demoSet.webpackConfig;
  const entryLib = path.resolve(demoSet.packagePath, config.entry);

  config.output = {
    filename: '[name].bundle.js',
    path: '/bundle/',
    publicPath: '/bundle/'
  };

  config.entry = _.chain(demoSet.demos)
  .mapValues(demo => demo.entry)
  .value();

  delete config.externals;

  return _.merge(config, {
    resolve: {
      alias: {
        [demoSet.packageName]: path.resolve(entryLib)
      }
    }
  });
}

export function serve(demoSet, {
  port = 8080,
  hostname = 'localhost'
} = {}) {
  const config = webpackConfig(demoSet);
  const server = express();

  server.use(webpackDevMiddleware(webpack(config), {
    publicPath: '/bundle/',
    headers: {
      'Access-Control-Allow-Origin': '*',
    }
  }));

  _.forEach(demoSet.demos, demo => {
    server.get(url.resolve('/', demo.relativePath), (req, res) => {
      renderFileAsync(demo.template, {
        title: demo.name,
        bundle: `/bundle/${demo.name}.bundle.js`
      }).then(html => {
        res.send(html);
      }).catch(() => {
        res.status(404).send(`template ${demo.template} not found`);
      });
    });
  });

  const httpServer = server.listen(port, hostname);
  const urlBase = `http://${hostname}:${port}`;
  const lines = [
    chalk.bold(`${chalk.green('The server is running at')} ${chalk.cyan.underline(urlBase)}`),
    `The following example pages are available:`
  ].concat(_.map(demoSet.demos, demo => {
    const urlDemo = url.resolve(`http://${hostname}:${port}/`, demo.relativePath);
    return `  ${chalk.magenta(demo.name)}: ${chalk.cyan.underline(urlDemo)}`;
  })).concat(['']);

  global.console.log(lines.join('\n'));

  return httpServer;
}
<|endoftext|>