<commit_before>// Copyright (c) 2015 Uber Technologies, Inc.

// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

'use strict';

var TypedError = require('error/typed');
var Duplex = require('stream').Duplex;
var util = require('util');

var reqres = require('../reqres');
var TChannelOutgoingRequest = reqres.OutgoingRequest;
var TChannelOutgoingResponse = reqres.OutgoingResponse;
var TChannelIncomingRequest = reqres.IncomingRequest;
var TChannelIncomingResponse = reqres.IncomingResponse;
var v2 = require('./index');

module.exports = TChannelV2Handler;

var TChannelUnhandledFrameTypeError = TypedError({
    type: 'tchannel.unhandled-frame-type',
    message: 'unhandled frame type {typeCode}',
    typeCode: null
});

var InvalidCodeStringError = TypedError({
    type: 'tchannel.invalid-code-string',
    message: 'Invalid Error frame code: {codeString}',
    codeString: null
});

function TChannelV2Handler(channel, options) {
    if (!(this instanceof TChannelV2Handler)) {
        return new TChannelV2Handler(channel, options);
    }
    var self = this;
    Duplex.call(self, {
        objectMode: true
    });
    self.channel = channel;
    self.remoteHostPort = null; // filled in by identify message
    self.lastSentFrameId = 0;
    // TODO: GC these... maybe that's up to TChannel itself wrt ops
    self.streamingReq = Object.create(null);
    self.streamingRes = Object.create(null);
}

util.inherits(TChannelV2Handler, Duplex);

TChannelV2Handler.prototype.nextFrameId = function nextFrameId() {
    var self = this;
    self.lastSentFrameId = (self.lastSentFrameId + 1) % v2.Frame.MaxId;
    return self.lastSentFrameId;
};

TChannelV2Handler.prototype._write = function _write(frame, encoding, callback) {
    var self = this;
    switch (frame.body.type) {
        case v2.Types.InitRequest:
            return self.handleInitRequest(frame, callback);
        case v2.Types.InitResponse:
            return self.handleInitResponse(frame, callback);
        case v2.Types.CallRequest:
            return self.handleCallRequest(frame, callback);
        case v2.Types.CallResponse:
            return self.handleCallResponse(frame, callback);
        case v2.Types.CallRequestCont:
            return self.handleCallRequestCont(frame, callback);
        case v2.Types.CallResponseCont:
            return self.handleCallResponseCont(frame, callback);
        case v2.Types.ErrorResponse:
            return self.handleError(frame, callback);
        default:
            return callback(TChannelUnhandledFrameTypeError({
                typeCode: frame.body.type
            }));
    }
};

TChannelV2Handler.prototype._read = function _read(/* n */) {
    /* noop */
};

TChannelV2Handler.prototype.handleInitRequest = function handleInitRequest(reqFrame, callback) {
    var self = this;
    if (self.remoteHostPort !== null) {
        return callback(new Error('duplicate init request')); // TODO typed error
    }
    /* jshint camelcase:false */
    var headers = reqFrame.body.headers;
    var init = {
        hostPort: headers.host_port,
        processName: headers.process_name
    };
    /* jshint camelcase:true */
    self.remoteHostPort = init.hostPort;
    self.emit('init.request', init);
    self.sendInitResponse(reqFrame);
    callback();
};

TChannelV2Handler.prototype.handleInitResponse = function handleInitResponse(resFrame, callback) {
    var self = this;
    if (self.remoteHostPort !== null) {
        return callback(new Error('duplicate init response')); // TODO typed error
    }
    /* jshint camelcase:false */
    var headers = resFrame.body.headers;
    var init = {
        hostPort: headers.host_port,
        processName: headers.process_name
    };
    /* jshint camelcase:true */
    self.remoteHostPort = init.hostPort;
    self.emit('init.response', init);
    callback();
};

TChannelV2Handler.prototype.handleCallRequest = function handleCallRequest(reqFrame, callback) {
    var self = this;
    if (self.remoteHostPort === null) {
        return callback(new Error('call request before init request')); // TODO typed error
    }
    var err = reqFrame.body.verifyChecksum();
    if (err) {
        callback(err); // TODO wrap context
        return;
    }
    var req = self.buildIncomingRequest(reqFrame);
    req.checksum = reqFrame.body.csum;
    if (req.state === reqres.States.Streaming) {
        self.streamingReq[req.id] = req;
    }
    self.emit('call.incoming.request', req);
    callback();
};

TChannelV2Handler.prototype.handleCallResponse = function handleCallResponse(resFrame, callback) {
    var self = this;
    if (self.remoteHostPort === null) {
        return callback(new Error('call response before init response')); // TODO typed error
    }
    var err = resFrame.body.verifyChecksum();
    if (err) {
        callback(err); // TODO wrap context
        return;
    }
    var res = self.buildIncomingResponse(resFrame);
    res.checksum = resFrame.body.csum;
    if (res.state === reqres.States.Streaming) {
        self.streamingRes[res.id] = res;
    }
    self.emit('call.incoming.response', res);
    callback();
};

TChannelV2Handler.prototype.handleCallRequestCont = function handleCallRequestCont(reqFrame, callback) {
    var self = this;
    if (self.remoteHostPort === null) {
        return callback(new Error('call request cont before init request')); // TODO typed error
    }

    var id = reqFrame.id;
    var req = self.streamingReq[id];
    if (!req) {
        return callback(new Error('call request cont for unknown request')); // TODO typed error
    }

    var checksum = req.checksum;
    if (checksum.type !== reqFrame.body.csum.type) {
        callback(new Error('checksum type changed mid-tream')); // TODO typed error
        return;
    }

    var err = reqFrame.body.verifyChecksum(checksum.val);
    if (err) {
        callback(err); // TODO wrap context
        return;
    }
    req.checksum = reqFrame.body.csum;
    self._handleCallFrame(req, reqFrame, callback);
};

TChannelV2Handler.prototype.handleCallResponseCont = function handleCallResponseCont(resFrame, callback) {
    var self = this;
    if (self.remoteHostPort === null) {
        return callback(new Error('call response cont before init response')); // TODO typed error
    }

    var id = resFrame.id;
    var res = self.streamingRes[id];
    if (!res) {
        return callback(new Error('call response cont for unknown response')); // TODO typed error
    }

    var checksum = res.checksum;
    if (checksum.type !== resFrame.body.csum.type) {
        callback(new Error('checksum type changed mid-tream')); // TODO typed error
        return;
    }

    var err = resFrame.body.verifyChecksum(checksum.val);
    if (err) {
        callback(err); // TODO wrap context
        return;
    }
    res.checksum = resFrame.body.csum;
    self._handleCallFrame(res, resFrame, callback);
};

TChannelV2Handler.prototype.handleError = function handleError(errFrame, callback) {
    var self = this;

    var id = errFrame.id;
    var code = errFrame.body.code;
    var message = String(errFrame.body.message);
    var err = v2.ErrorResponse.CodeErrors[code]({
        originalId: id,
        message: message
    });
    if (id === v2.Frame.NullId) {
        // fatal error not associated with a prior frame
        callback(err);
    } else {
        self.emit('call.incoming.error', err);
        callback();
    }
};

TChannelV2Handler.prototype._handleCallFrame = function _handleCallFrame(r, frame, callback) {
    if (r.state === reqres.States.Done) {
        callback(new Error('got cont in done state')); // TODO typed error
        return;
    }

    if (r.state === reqres.States.Initial) {
        callback(new Error('got cont to initial req')); // TODO typed error
        return;
    }

    callback(new Error('not implemented'));
};

TChannelV2Handler.prototype.sendInitRequest = function sendInitRequest() {
    var self = this;
    var id = self.nextFrameId(); // TODO: assert(id === 1)?
    var hostPort = self.channel.hostPort;
    var processName = self.channel.processName;
    var body = v2.InitRequest(v2.VERSION, {
        /* jshint camelcase:false */
        host_port: hostPort,
        process_name: processName
        /* jshint camelcase:true */
    });
    var reqFrame = v2.Frame(id, body);
    self.push(reqFrame);
};

TChannelV2Handler.prototype.sendInitResponse = function sendInitResponse(reqFrame) {
    var self = this;
    var id = reqFrame.id;
    var hostPort = self.channel.hostPort;
    var processName = self.channel.processName;
    var body = v2.InitResponse(v2.VERSION, {
        /* jshint camelcase:false */
        host_port: hostPort,
        process_name: processName
        /* jshint camelcase:true */
    });
    var resFrame = v2.Frame(id, body);
    self.push(resFrame);
};

TChannelV2Handler.prototype.sendCallRequestFrame = function sendCallRequestFrame(req, flags, args) {
    var self = this;
    var reqBody = v2.CallRequest(flags, req.ttl, req.tracing, req.service, req.headers, req.checksum.type);
    req.checksum = self._sendCallBodies(req.id, reqBody.csum, reqBody, args);
};

TChannelV2Handler.prototype.sendCallResponseFrame = function sendCallResponseFrame(res, flags, args) {
    var self = this;
    var code = res.ok ? v2.CallResponse.Codes.OK : v2.CallResponse.Codes.Error;
    var resBody = v2.CallResponse(flags, code, res.tracing, res.headers, res.checksum.type);
    res.checksum = self._sendCallBodies(res.id, resBody.csum, resBody, args);
};

TChannelV2Handler.prototype.sendCallRequestContFrame = function sendCallRequestContFrame(req, flags, args) {
    var self = this;
    var reqBody = v2.CallRequestCont(flags, req.checksum.type);
    req.checksum = self._sendCallBodies(req.id, req.checksum, reqBody, args);
};

TChannelV2Handler.prototype.sendCallResponseContFrame = function sendCallResponseContFrame(res, flags, args) {
    var self = this;
    var resBody = v2.CallResponseCont(flags, res.checksum.type);
    res.checksum = self._sendCallBodies(res.id, res.checksum, resBody, args);
};

TChannelV2Handler.prototype._sendCallBodies = function _sendCallBodies(id, checksum, body, args) {
    var self = this;
    var bodies = body.splitArgs(args, v2.Frame.MaxBodySize);
    if (bodies.length > 1) throw new Error('not implemented');
    for (var i = 0; i < bodies.length; i++) {
        body = bodies[i];
        body.updateChecksum(checksum.val);
        checksum = body.csum;
        var frame = v2.Frame(id, body);
        self.push(frame);
    }
    return checksum;
};

TChannelV2Handler.prototype.sendErrorFrame = function sendErrorFrame(req, codeString, message) {
    var self = this;

    var code = v2.ErrorResponse.Codes[codeString];
    if (code === undefined) {
        throw InvalidCodeStringError({
            codeString: codeString
        });
    }

    var errBody = v2.ErrorResponse(code, req.id, message);
    var errFrame = v2.Frame(req.id, errBody);
    self.push(errFrame);
};

TChannelV2Handler.prototype.buildOutgoingRequest = function buildOutgoingRequest(options) {
    var self = this;
    var id = self.nextFrameId();
    if (options.checksumType === undefined || options.checksumType === null) {
        options.checksumType = v2.Checksum.Types.FarmHash32;
    }
    options.checksum = v2.Checksum(options.checksumType);
    options.sendFrame = {
        callRequest: sendCallRequestFrame,
        callRequestCont: sendCallRequestContFrame
    };
    var req = TChannelOutgoingRequest(id, options);
    return req;

    function sendCallRequestFrame(args, isLast) {
        var flags = 0;
        if (!isLast) throw new Error('not implemented');
        self.sendCallRequestFrame(req, flags, args);
    }

    function sendCallRequestContFrame(args, isLast) {
        var flags = 0;
        if (!isLast) flags |= v2.CallResponse.Flags.Fragment;
        self.sendCallRequestContFrame(req, flags, args);
    }
};

TChannelV2Handler.prototype.buildOutgoingResponse = function buildOutgoingResponse(req) {
    var self = this;
    var res = TChannelOutgoingResponse(req.id, {
        tracing: req.tracing,
        headers: {},
        checksumType: req.checksumType,
        checksum: v2.Checksum(req.checksumType),
        arg1: req.arg1,
        sendFrame: {
            callResponse: sendCallResponseFrame,
            callResponseCont: sendCallResponseContFrame,
            error: sendErrorFrame
        }
    });
    return res;

    function sendCallResponseFrame(args, isLast) {
        var flags = 0;
        if (!isLast) throw new Error('not implemented');
        self.sendCallResponseFrame(res, flags, args);
    }

    function sendCallResponseContFrame(args, isLast) {
        var flags = 0;
        if (!isLast) flags |= v2.CallResponse.Flags.Fragment;
        self.sendCallResponseContFrame(res, flags, args);
    }

    function sendErrorFrame(codeString, message) {
        self.sendErrorFrame(req, codeString, message);
    }
};

TChannelV2Handler.prototype.buildIncomingRequest = function buildIncomingRequest(reqFrame) {
    var req = TChannelIncomingRequest(reqFrame.id, {
        ttl: reqFrame.body.ttl,
        tracing: reqFrame.body.tracing,
        service: reqFrame.body.service,
        headers: reqFrame.body.headers,
        checksum: v2.Checksum(reqFrame.body.csum.type)
    });
    req.handleFrame(reqFrame.body.args);
    return req;
};

TChannelV2Handler.prototype.buildIncomingResponse = function buildIncomingResponse(resFrame) {
    var res = TChannelIncomingResponse(resFrame.id, {
        code: resFrame.body.code,
        checksum: v2.Checksum(resFrame.body.csum.type)
    });
    res.handleFrame(resFrame.body.args);
    return res;
};
<commit_msg>v2/handler: move cont checksumming into _handleCallFrame<commit_after>// Copyright (c) 2015 Uber Technologies, Inc.

// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

'use strict';

var TypedError = require('error/typed');
var Duplex = require('stream').Duplex;
var util = require('util');

var reqres = require('../reqres');
var TChannelOutgoingRequest = reqres.OutgoingRequest;
var TChannelOutgoingResponse = reqres.OutgoingResponse;
var TChannelIncomingRequest = reqres.IncomingRequest;
var TChannelIncomingResponse = reqres.IncomingResponse;
var v2 = require('./index');

module.exports = TChannelV2Handler;

var TChannelUnhandledFrameTypeError = TypedError({
    type: 'tchannel.unhandled-frame-type',
    message: 'unhandled frame type {typeCode}',
    typeCode: null
});

var InvalidCodeStringError = TypedError({
    type: 'tchannel.invalid-code-string',
    message: 'Invalid Error frame code: {codeString}',
    codeString: null
});

function TChannelV2Handler(channel, options) {
    if (!(this instanceof TChannelV2Handler)) {
        return new TChannelV2Handler(channel, options);
    }
    var self = this;
    Duplex.call(self, {
        objectMode: true
    });
    self.channel = channel;
    self.remoteHostPort = null; // filled in by identify message
    self.lastSentFrameId = 0;
    // TODO: GC these... maybe that's up to TChannel itself wrt ops
    self.streamingReq = Object.create(null);
    self.streamingRes = Object.create(null);
}

util.inherits(TChannelV2Handler, Duplex);

TChannelV2Handler.prototype.nextFrameId = function nextFrameId() {
    var self = this;
    self.lastSentFrameId = (self.lastSentFrameId + 1) % v2.Frame.MaxId;
    return self.lastSentFrameId;
};

TChannelV2Handler.prototype._write = function _write(frame, encoding, callback) {
    var self = this;
    switch (frame.body.type) {
        case v2.Types.InitRequest:
            return self.handleInitRequest(frame, callback);
        case v2.Types.InitResponse:
            return self.handleInitResponse(frame, callback);
        case v2.Types.CallRequest:
            return self.handleCallRequest(frame, callback);
        case v2.Types.CallResponse:
            return self.handleCallResponse(frame, callback);
        case v2.Types.CallRequestCont:
            return self.handleCallRequestCont(frame, callback);
        case v2.Types.CallResponseCont:
            return self.handleCallResponseCont(frame, callback);
        case v2.Types.ErrorResponse:
            return self.handleError(frame, callback);
        default:
            return callback(TChannelUnhandledFrameTypeError({
                typeCode: frame.body.type
            }));
    }
};

TChannelV2Handler.prototype._read = function _read(/* n */) {
    /* noop */
};

TChannelV2Handler.prototype.handleInitRequest = function handleInitRequest(reqFrame, callback) {
    var self = this;
    if (self.remoteHostPort !== null) {
        return callback(new Error('duplicate init request')); // TODO typed error
    }
    /* jshint camelcase:false */
    var headers = reqFrame.body.headers;
    var init = {
        hostPort: headers.host_port,
        processName: headers.process_name
    };
    /* jshint camelcase:true */
    self.remoteHostPort = init.hostPort;
    self.emit('init.request', init);
    self.sendInitResponse(reqFrame);
    callback();
};

TChannelV2Handler.prototype.handleInitResponse = function handleInitResponse(resFrame, callback) {
    var self = this;
    if (self.remoteHostPort !== null) {
        return callback(new Error('duplicate init response')); // TODO typed error
    }
    /* jshint camelcase:false */
    var headers = resFrame.body.headers;
    var init = {
        hostPort: headers.host_port,
        processName: headers.process_name
    };
    /* jshint camelcase:true */
    self.remoteHostPort = init.hostPort;
    self.emit('init.response', init);
    callback();
};

TChannelV2Handler.prototype.handleCallRequest = function handleCallRequest(reqFrame, callback) {
    var self = this;
    if (self.remoteHostPort === null) {
        return callback(new Error('call request before init request')); // TODO typed error
    }
    var err = reqFrame.body.verifyChecksum();
    if (err) {
        callback(err); // TODO wrap context
        return;
    }
    var req = self.buildIncomingRequest(reqFrame);
    req.checksum = reqFrame.body.csum;
    if (req.state === reqres.States.Streaming) {
        self.streamingReq[req.id] = req;
    }
    self.emit('call.incoming.request', req);
    callback();
};

TChannelV2Handler.prototype.handleCallResponse = function handleCallResponse(resFrame, callback) {
    var self = this;
    if (self.remoteHostPort === null) {
        return callback(new Error('call response before init response')); // TODO typed error
    }
    var err = resFrame.body.verifyChecksum();
    if (err) {
        callback(err); // TODO wrap context
        return;
    }
    var res = self.buildIncomingResponse(resFrame);
    res.checksum = resFrame.body.csum;
    if (res.state === reqres.States.Streaming) {
        self.streamingRes[res.id] = res;
    }
    self.emit('call.incoming.response', res);
    callback();
};

TChannelV2Handler.prototype.handleCallRequestCont = function handleCallRequestCont(reqFrame, callback) {
    var self = this;
    if (self.remoteHostPort === null) {
        return callback(new Error('call request cont before init request')); // TODO typed error
    }
    var id = reqFrame.id;
    var req = self.streamingReq[id];
    if (!req) {
        return callback(new Error('call request cont for unknown request')); // TODO typed error
    }
    self._handleCallFrame(req, reqFrame, callback);
};

TChannelV2Handler.prototype.handleCallResponseCont = function handleCallResponseCont(resFrame, callback) {
    var self = this;
    if (self.remoteHostPort === null) {
        return callback(new Error('call response cont before init response')); // TODO typed error
    }
    var id = resFrame.id;
    var res = self.streamingRes[id];
    if (!res) {
        return callback(new Error('call response cont for unknown response')); // TODO typed error
    }
    self._handleCallFrame(res, resFrame, callback);
};

TChannelV2Handler.prototype.handleError = function handleError(errFrame, callback) {
    var self = this;

    var id = errFrame.id;
    var code = errFrame.body.code;
    var message = String(errFrame.body.message);
    var err = v2.ErrorResponse.CodeErrors[code]({
        originalId: id,
        message: message
    });
    if (id === v2.Frame.NullId) {
        // fatal error not associated with a prior frame
        callback(err);
    } else {
        self.emit('call.incoming.error', err);
        callback();
    }
};

TChannelV2Handler.prototype._handleCallFrame = function _handleCallFrame(r, frame, callback) {
    if (r.state === reqres.States.Done) {
        callback(new Error('got cont in done state')); // TODO typed error
        return;
    }

    var checksum = r.checksum;
    if (checksum.type !== frame.body.csum.type) {
        callback(new Error('checksum type changed mid-tream')); // TODO typed error
        return;
    }

    var err = frame.body.verifyChecksum(checksum.val);
    if (err) {
        callback(err); // TODO wrap context
        return;
    }
    r.checksum = frame.body.csum;

    if (r.state === reqres.States.Initial) {
        callback(new Error('got cont to initial req')); // TODO typed error
        return;
    }

    callback(new Error('not implemented'));
};

TChannelV2Handler.prototype.sendInitRequest = function sendInitRequest() {
    var self = this;
    var id = self.nextFrameId(); // TODO: assert(id === 1)?
    var hostPort = self.channel.hostPort;
    var processName = self.channel.processName;
    var body = v2.InitRequest(v2.VERSION, {
        /* jshint camelcase:false */
        host_port: hostPort,
        process_name: processName
        /* jshint camelcase:true */
    });
    var reqFrame = v2.Frame(id, body);
    self.push(reqFrame);
};

TChannelV2Handler.prototype.sendInitResponse = function sendInitResponse(reqFrame) {
    var self = this;
    var id = reqFrame.id;
    var hostPort = self.channel.hostPort;
    var processName = self.channel.processName;
    var body = v2.InitResponse(v2.VERSION, {
        /* jshint camelcase:false */
        host_port: hostPort,
        process_name: processName
        /* jshint camelcase:true */
    });
    var resFrame = v2.Frame(id, body);
    self.push(resFrame);
};

TChannelV2Handler.prototype.sendCallRequestFrame = function sendCallRequestFrame(req, flags, args) {
    var self = this;
    var reqBody = v2.CallRequest(flags, req.ttl, req.tracing, req.service, req.headers, req.checksum.type);
    req.checksum = self._sendCallBodies(req.id, reqBody.csum, reqBody, args);
};

TChannelV2Handler.prototype.sendCallResponseFrame = function sendCallResponseFrame(res, flags, args) {
    var self = this;
    var code = res.ok ? v2.CallResponse.Codes.OK : v2.CallResponse.Codes.Error;
    var resBody = v2.CallResponse(flags, code, res.tracing, res.headers, res.checksum.type);
    res.checksum = self._sendCallBodies(res.id, resBody.csum, resBody, args);
};

TChannelV2Handler.prototype.sendCallRequestContFrame = function sendCallRequestContFrame(req, flags, args) {
    var self = this;
    var reqBody = v2.CallRequestCont(flags, req.checksum.type);
    req.checksum = self._sendCallBodies(req.id, req.checksum, reqBody, args);
};

TChannelV2Handler.prototype.sendCallResponseContFrame = function sendCallResponseContFrame(res, flags, args) {
    var self = this;
    var resBody = v2.CallResponseCont(flags, res.checksum.type);
    res.checksum = self._sendCallBodies(res.id, res.checksum, resBody, args);
};

TChannelV2Handler.prototype._sendCallBodies = function _sendCallBodies(id, checksum, body, args) {
    var self = this;
    var bodies = body.splitArgs(args, v2.Frame.MaxBodySize);
    if (bodies.length > 1) throw new Error('not implemented');
    for (var i = 0; i < bodies.length; i++) {
        body = bodies[i];
        body.updateChecksum(checksum.val);
        checksum = body.csum;
        var frame = v2.Frame(id, body);
        self.push(frame);
    }
    return checksum;
};

TChannelV2Handler.prototype.sendErrorFrame = function sendErrorFrame(req, codeString, message) {
    var self = this;

    var code = v2.ErrorResponse.Codes[codeString];
    if (code === undefined) {
        throw InvalidCodeStringError({
            codeString: codeString
        });
    }

    var errBody = v2.ErrorResponse(code, req.id, message);
    var errFrame = v2.Frame(req.id, errBody);
    self.push(errFrame);
};

TChannelV2Handler.prototype.buildOutgoingRequest = function buildOutgoingRequest(options) {
    var self = this;
    var id = self.nextFrameId();
    if (options.checksumType === undefined || options.checksumType === null) {
        options.checksumType = v2.Checksum.Types.FarmHash32;
    }
    options.checksum = v2.Checksum(options.checksumType);
    options.sendFrame = {
        callRequest: sendCallRequestFrame,
        callRequestCont: sendCallRequestContFrame
    };
    var req = TChannelOutgoingRequest(id, options);
    return req;

    function sendCallRequestFrame(args, isLast) {
        var flags = 0;
        if (!isLast) throw new Error('not implemented');
        self.sendCallRequestFrame(req, flags, args);
    }

    function sendCallRequestContFrame(args, isLast) {
        var flags = 0;
        if (!isLast) flags |= v2.CallResponse.Flags.Fragment;
        self.sendCallRequestContFrame(req, flags, args);
    }
};

TChannelV2Handler.prototype.buildOutgoingResponse = function buildOutgoingResponse(req) {
    var self = this;
    var res = TChannelOutgoingResponse(req.id, {
        tracing: req.tracing,
        headers: {},
        checksumType: req.checksumType,
        checksum: v2.Checksum(req.checksumType),
        arg1: req.arg1,
        sendFrame: {
            callResponse: sendCallResponseFrame,
            callResponseCont: sendCallResponseContFrame,
            error: sendErrorFrame
        }
    });
    return res;

    function sendCallResponseFrame(args, isLast) {
        var flags = 0;
        if (!isLast) throw new Error('not implemented');
        self.sendCallResponseFrame(res, flags, args);
    }

    function sendCallResponseContFrame(args, isLast) {
        var flags = 0;
        if (!isLast) flags |= v2.CallResponse.Flags.Fragment;
        self.sendCallResponseContFrame(res, flags, args);
    }

    function sendErrorFrame(codeString, message) {
        self.sendErrorFrame(req, codeString, message);
    }
};

TChannelV2Handler.prototype.buildIncomingRequest = function buildIncomingRequest(reqFrame) {
    var req = TChannelIncomingRequest(reqFrame.id, {
        ttl: reqFrame.body.ttl,
        tracing: reqFrame.body.tracing,
        service: reqFrame.body.service,
        headers: reqFrame.body.headers,
        checksum: v2.Checksum(reqFrame.body.csum.type)
    });
    req.handleFrame(reqFrame.body.args);
    return req;
};

TChannelV2Handler.prototype.buildIncomingResponse = function buildIncomingResponse(resFrame) {
    var res = TChannelIncomingResponse(resFrame.id, {
        code: resFrame.body.code,
        checksum: v2.Checksum(resFrame.body.csum.type)
    });
    res.handleFrame(resFrame.body.args);
    return res;
};
<|endoftext|>


<commit_before>function JackPromotion(session){
	this.canRepeat = false;
	this.session = session;
	this.playerList = [];  //what players are already in the medium when i trigger?

	this.trigger = function(playerList){
		this.playerList = playerList;
		return (this.session.queen.crowned == null) && (this.session.jack.getHP() > 0) && (this.session.jack.getPower() > this.session.queen.getPower());
	}

	this.addImportantEvent = function(){
		var current_mvp =  findStrongestPlayer(this.session.players)
		return this.session.addImportantEvent(new JackPromoted(this.session, current_mvp.power) );
	}

	this.content = function(){
		this.session.jack.crowned = this.session.queensRing;
		this.session.queen.crowned = null;
		var ret = " In a shocking turn of events, Jack Noir claims the Black Queen's RING OF ORBS " + this.session.convertPlayerNumberToWords();
		ret += "FOLD. "
		if(this.session.queen.crowned && !this.session.queen.exiled){
			if(this.session.queen.getHP() > 0){
				ret += "He easily defeats the weakened queen and uses her ring to obtain her power. ";
				this.session.queen.currentHP = -9999; //actually kill her you dunkass.
			}else{
				ret += " He pries the ring off her still twitching finger. ";
			}
		}else{
			ret += "It's not hard at all to get his Crew to pull off a heist to get the RING OF ORBS "+ this.session.convertPlayerNumberToWords();
			ret += "FOLD. "
			if(this.session.queen.getHP() > 0 && !this.session.queen.exiled){
				ret += "He easily defeats the weakened queen while he's at it. ";
			}
		}
		ret += " You'd think this would be no worse than having the Black Queen around, but Jack is kind of a big deal. ";
		ret += " He immediately decides to show everybody his stabs. ";
		var badPrototyping = findBadPrototyping(this.playerList);

		if( badPrototyping == "First Guardian"){
			ret += " He is now in charge of random teleporation murders. ";
		}
		return ret;
	}

	this.renderContent = function(div){
		var alt = this.addImportantEvent();
		//console.log("Alt for jack promotion is: " + alt);
		if(alt && alt.alternateScene(div)){
			return;
		}
		div.append("
"+this.content());
	}

}
<commit_msg>slight refactoring to ways jack can be refactored. you're supposed to KILL her not kISS her you dunkass<commit_after>function JackPromotion(session){
	this.canRepeat = false;
	this.session = session;
	this.playerList = [];  //what players are already in the medium when i trigger?

	this.trigger = function(playerList){
		this.playerList = playerList;
		if(this.session.jack.getHP() <= 0) return false;  //jack can't be dead.
		if(this.session.queensRing == null); return false; //all is moot if no ring
		if(this.session.jack.crowned != null); return false; //don't steal the ring from yourself, dunkass

		//jack is alive, and stronger than queen. (even if queen is dead, this means her lackeys are undisciplined)
		if(((this.session.jack.getPower() > this.session.queen.getPower())){
			return true;
		}

		return false;
	}

	this.addImportantEvent = function(){
		var current_mvp =  findStrongestPlayer(this.session.players)
		return this.session.addImportantEvent(new JackPromoted(this.session, current_mvp.power) );
	}

	this.content = function(){
		this.session.jack.crowned = this.session.queensRing;
		this.session.queen.crowned = null;
		var ret = " In a shocking turn of events, Jack Noir claims the Black Queen's RING OF ORBS " + this.session.convertPlayerNumberToWords();
		ret += "FOLD. "
		if(this.session.queen.crowned && !this.session.queen.exiled){
			if(this.session.queen.getHP() > 0){
				if(Math.seededRandom() > .5){
					//and now the players still have to fight her.  ringless sure, but....
					this.session.queen.power = 50; //she gets a morale boost, any weakening she had is reduced.
					ret += " At this point you would EXPECT him to kill the weakened Queen, but somehow they end up making out??? Dersites, am I right?  He still ends up with the RING, though."
				}else{
					ret += "He easily murders the weakened queen and uses her ring to obtain her power. ";
					this.session.queen.currentHP = -9999; //actually kill her you dunkass. not KISS her.
				}

			}else{
				ret += " He pries the ring off her still twitching finger. ";
			}
		}else{
			ret += "It's not hard at all to get his Crew to pull off a heist to get the RING OF ORBS "+ this.session.convertPlayerNumberToWords();
			ret += "FOLD. "
			if(this.session.queen.getHP() > 0 && !this.session.queen.exiled){
				ret += "He easily defeats the weakened queen while he's at it. ";
			}
		}
		ret += " You'd think this would be no worse than having the Black Queen around, but Jack is kind of a big deal. ";
		ret += " He immediately decides to show everybody his stabs. ";
		var badPrototyping = findBadPrototyping(this.playerList);

		if( badPrototyping == "First Guardian"){
			ret += " He is now in charge of random teleporation murders. ";
		}
		return ret;
	}

	this.renderContent = function(div){
		var alt = this.addImportantEvent();
		//console.log("Alt for jack promotion is: " + alt);
		if(alt && alt.alternateScene(div)){
			return;
		}
		div.append("
"+this.content());
	}

}
<|endoftext|>


<commit_before>'use strict';

const EmberAddon = require('ember-cli/lib/broccoli/ember-addon');
const urls = require('./lib/prember-urls');

module.exports = function(defaults) {
  var includePolyfill = process.env.EMBER_ENV === 'production' || process.env.CI;

  var babelOptions = {};
  if (includePolyfill) {
    babelOptions.includePolyfill = true;
  } else {
    babelOptions.exclude = ['@babel/plugin-transform-regenerator'];
  }

  var app = new EmberAddon(defaults, {
    minifyJS: {
      enabled: false
    },

    snippetPaths: ['tests/dummy/snippets'],
    snippetSearchPaths: ['app', 'tests/dummy/app', 'addon'],

    emberCliFontAwesome: {
      useScss: true
    },

    babel: babelOptions,

    prember: {
      urls,
      // GitHub Pages uses this filename to serve 404s
      emptyFile: '404.html'
    }
  });

  /*
    This build file specifes the options for the dummy test app of this
    addon, located in `/tests/dummy`
    This build file does *not* influence how the addon or the app using it
    behave. You most likely want to be modifying `./index.js` or app's build file
  */

  app.import('vendor/dummy-deps/rx.js');

  return app.toTree();
};
<commit_msg>Fix polyfill inclusion for dummy site<commit_after>'use strict';

const EmberAddon = require('ember-cli/lib/broccoli/ember-addon');
const urls = require('./lib/prember-urls');

module.exports = function(defaults) {
  let includePolyfill = process.env.EMBER_ENV === 'production';

  let babelOptions = {};
  if (!includePolyfill) {
    babelOptions.exclude = ['@babel/plugin-transform-regenerator'];
  }

  let app = new EmberAddon(defaults, {
    minifyJS: {
      enabled: false
    },

    snippetPaths: ['tests/dummy/snippets'],
    snippetSearchPaths: ['app', 'tests/dummy/app', 'addon'],

    emberCliFontAwesome: {
      useScss: true
    },

    babel: babelOptions,

    'ember-cli-babel': {
      includePolyfill: process.env.EMBER_ENV === 'production'
    },

    prember: {
      urls,
      // GitHub Pages uses this filename to serve 404s
      emptyFile: '404.html'
    }
  });

  /*
    This build file specifes the options for the dummy test app of this
    addon, located in `/tests/dummy`
    This build file does *not* influence how the addon or the app using it
    behave. You most likely want to be modifying `./index.js` or app's build file
  */

  app.import('vendor/dummy-deps/rx.js');

  return app.toTree();
};
<|endoftext|>


<commit_before>/* global require, module */
var EmberApp = require('ember-cli/lib/broccoli/ember-app');

module.exports = function(defaults) {
  var app = new EmberApp(defaults, {
    fingerprint: {
      extensions: ['js', 'css', 'png', 'jpg', 'jpeg', 'gif', 'map', 'pdf']
    },
    'ember-cli-bootstrap-sassy': {
      js: ['affix','collapse']
    }
  });

  // Use `app.import` to add additional libraries to the generated
  // output files.
  //
  // If you need to use different assets in different
  // environments, specify an object as the first parameter. That
  // object's keys should be the environment name and the values
  // should be the asset to use in that environment.
  //
  // If the library that you are including contains AMD or ES6
  // modules that you would like to import into your application
  // please specify an object with the list of modules as keys
  // along with the exports of each module as its value.

  app.import('bower_components/tinycolor/tinycolor.js');
  app.import('bower_components/jquery-mousewheel/jquery.mousewheel.js');
  app.import('bower_components/simplr-smoothscroll/lib/jquery.simplr.smoothscroll.js');

  return app.toTree();
};
<commit_msg>disable sri<commit_after>/* global require, module */
var EmberApp = require('ember-cli/lib/broccoli/ember-app');

module.exports = function(defaults) {
  var app = new EmberApp(defaults, {
    SRI: {
      enabled: false
    },
    fingerprint: {
      extensions: ['js', 'css', 'png', 'jpg', 'jpeg', 'gif', 'map', 'pdf']
    },
    'ember-cli-bootstrap-sassy': {
      js: ['affix','collapse']
    }
  });

  // Use `app.import` to add additional libraries to the generated
  // output files.
  //
  // If you need to use different assets in different
  // environments, specify an object as the first parameter. That
  // object's keys should be the environment name and the values
  // should be the asset to use in that environment.
  //
  // If the library that you are including contains AMD or ES6
  // modules that you would like to import into your application
  // please specify an object with the list of modules as keys
  // along with the exports of each module as its value.

  app.import('bower_components/tinycolor/tinycolor.js');
  app.import('bower_components/jquery-mousewheel/jquery.mousewheel.js');
  app.import('bower_components/simplr-smoothscroll/lib/jquery.simplr.smoothscroll.js');

  return app.toTree();
};
<|endoftext|>


<commit_before>'use strict';

const EmberAddon = require('ember-cli/lib/broccoli/ember-addon');

module.exports = function(defaults) {
  var app = new EmberAddon(defaults, {
    includeHighlightJS: false,
    includeHighlightStyle: false,
    snippetSearchPaths: ['app', 'tests']
  });

  /*
    As recommended by ember-cli-snippet this is a customised download from
    https://highlightjs.org/download/
    with CSS, Javscript, HTML/XML, Handlebars
  */

  app.import('vendor/highlight.pack.js', {
    using: [ { transformation: 'amd', as: 'highlight.js' } ]
  });

  return app.toTree();
};
<commit_msg>Fix github pages build<commit_after>'use strict';

const EmberAddon = require('ember-cli/lib/broccoli/ember-addon');

module.exports = function(defaults) {
  var app = new EmberAddon(defaults, {
    includeHighlightJS: false,
    includeHighlightStyle: false,
    snippetSearchPaths: ['app', 'tests'],

    minifyCSS: {
      enabled: false // CSS minification w/ @import rules seems to be broken in Ember-CLI 3.3
    }
  });

  /*
    As recommended by ember-cli-snippet this is a customised download from
    https://highlightjs.org/download/
    with CSS, Javscript, HTML/XML, Handlebars
  */

  app.import('vendor/highlight.pack.js', {
    using: [ { transformation: 'amd', as: 'highlight.js' } ]
  });

  return app.toTree();
};
<|endoftext|>


<commit_before>/* globals describe, it, expect */

import Tokenizer from '../../../src/js/source/tokenizer.js'

describe("Source/Tokenizer", function () {
    it("tokenizes an empty string", function () {
        var tokenizer = new Tokenizer()

        expect(tokenizer.tokenizeString(""))
            .toEqual([[]])
    })

    it("tokenizes common keywords", function () {
        var tokenizer = new Tokenizer()

        expect(tokenizer.tokenizeString("public function test() {}")[0])
            .toEqual([
                { type: 'keyword', text: 'public' },
                { type: 'whitespace', text: ' ' },
                { type: 'keyword', text: 'function' },
                { type: 'whitespace', text: ' ' },
                { type: 'name', text: 'test' },
                { type: 'operator', text: '(' },
                { type: 'operator', text: ')' },
                { type: 'whitespace', text: ' ' },
                { type: 'operator', text: '{' },
                { type: 'operator', text: ' }' },
            ])
    })

    it("tokenizes single quoted string with escape sequence", function () {
        var tokenizer = new Tokenizer()

        expect(tokenizer.tokenizeString("' Hello \\' \\ \" world \\\\'")[0])
            .toEqual([
                { type: 'string', text: "' Hello \\' \\ \" world \\\\'" },
            ])
    })

    it("tokenizes double quoted string with escape sequence", function () {
        var tokenizer = new Tokenizer()

        expect(tokenizer.tokenizeString('" Hello \\" \\ \' world \\\\"')[0])
            .toEqual([
                { type: 'string', text: '" Hello \\" \\ \' world \\\\"' },
            ])
    })

    it("tokenizes simple integer number", function () {
        var tokenizer = new Tokenizer()

        expect(tokenizer.tokenizeString('12345')[0])
            .toEqual([
                { type: 'number', text: '12345' },
            ])
    })

    it("tokenizes simple float number", function () {
        var tokenizer = new Tokenizer()

        expect(tokenizer.tokenizeString('-1.345')[0])
            .toEqual([
                { type: 'number', text: '-1.345' },
            ])
    })

    it("tokenizes dot prefixed float number", function () {
        var tokenizer = new Tokenizer()

        expect(tokenizer.tokenizeString('+.345')[0])
            .toEqual([
                { type: 'number', text: '+.345' },
            ])
    })

    it("tokenizes // comment", function () {
        var tokenizer = new Tokenizer()

        expect(tokenizer.tokenizeString("// foo\n'bar'")[0])
            .toEqual([
                { type: 'comment', text: '// foo' },
            ])
    })

    it("tokenizes # comment", function () {
        var tokenizer = new Tokenizer()

        expect(tokenizer.tokenizeString("# foo\n'bar'")[0])
            .toEqual([
                { type: 'comment', text: '# foo' },
            ])
    })

    it("tokenizes multiline string", function () {
        var tokenizer = new Tokenizer()

        expect(tokenizer.tokenizeString("'foo\nbar\r\nblubb\rfoobar'"))
            .toEqual([
                [{ type: 'string', text: '\'foo' }],
                [{ type: 'string', text: 'bar' }],
                [{ type: 'string', text: 'blubb' }],
                [{ type: 'string', text: 'foobar\'' }],
            ])
    })

    it("tokenizes multiline comment", function () {
        var tokenizer = new Tokenizer()

        expect(tokenizer.tokenizeString("/* foo\nbar */"))
            .toEqual([
                [{ type: 'comment', text: '/* foo' }],
                [{ type: 'comment', text: 'bar */' }],
            ])
    })

    it("tokenizes two comments comment", function () {
        var tokenizer = new Tokenizer()

        expect(tokenizer.tokenizeString("/* foo *//* bar */")[0])
            .toEqual([
                { type: 'comment', text: '/* foo */' },
                { type: 'comment', text: '/* bar */' },
            ])
    })
})
<commit_msg>Fixed broken assert<commit_after>/* globals describe, it, expect */

import Tokenizer from '../../../src/js/source/tokenizer.js'

describe("Source/Tokenizer", function () {
    it("tokenizes an empty string", function () {
        var tokenizer = new Tokenizer()

        expect(tokenizer.tokenizeString(""))
            .toEqual([[]])
    })

    it("tokenizes common keywords", function () {
        var tokenizer = new Tokenizer()

        expect(tokenizer.tokenizeString("public function test() {}")[0])
            .toEqual([
                { type: 'keyword', text: 'public' },
                { type: 'whitespace', text: ' ' },
                { type: 'keyword', text: 'function' },
                { type: 'whitespace', text: ' ' },
                { type: 'name', text: 'test' },
                { type: 'operator', text: '(' },
                { type: 'operator', text: ')' },
                { type: 'whitespace', text: ' ' },
                { type: 'operator', text: '{' },
                { type: 'operator', text: '}' },
            ])
    })

    it("tokenizes single quoted string with escape sequence", function () {
        var tokenizer = new Tokenizer()

        expect(tokenizer.tokenizeString("' Hello \\' \\ \" world \\\\'")[0])
            .toEqual([
                { type: 'string', text: "' Hello \\' \\ \" world \\\\'" },
            ])
    })

    it("tokenizes double quoted string with escape sequence", function () {
        var tokenizer = new Tokenizer()

        expect(tokenizer.tokenizeString('" Hello \\" \\ \' world \\\\"')[0])
            .toEqual([
                { type: 'string', text: '" Hello \\" \\ \' world \\\\"' },
            ])
    })

    it("tokenizes simple integer number", function () {
        var tokenizer = new Tokenizer()

        expect(tokenizer.tokenizeString('12345')[0])
            .toEqual([
                { type: 'number', text: '12345' },
            ])
    })

    it("tokenizes simple float number", function () {
        var tokenizer = new Tokenizer()

        expect(tokenizer.tokenizeString('-1.345')[0])
            .toEqual([
                { type: 'number', text: '-1.345' },
            ])
    })

    it("tokenizes dot prefixed float number", function () {
        var tokenizer = new Tokenizer()

        expect(tokenizer.tokenizeString('+.345')[0])
            .toEqual([
                { type: 'number', text: '+.345' },
            ])
    })

    it("tokenizes // comment", function () {
        var tokenizer = new Tokenizer()

        expect(tokenizer.tokenizeString("// foo\n'bar'")[0])
            .toEqual([
                { type: 'comment', text: '// foo' },
            ])
    })

    it("tokenizes # comment", function () {
        var tokenizer = new Tokenizer()

        expect(tokenizer.tokenizeString("# foo\n'bar'")[0])
            .toEqual([
                { type: 'comment', text: '# foo' },
            ])
    })

    it("tokenizes multiline string", function () {
        var tokenizer = new Tokenizer()

        expect(tokenizer.tokenizeString("'foo\nbar\r\nblubb\rfoobar'"))
            .toEqual([
                [{ type: 'string', text: '\'foo' }],
                [{ type: 'string', text: 'bar' }],
                [{ type: 'string', text: 'blubb' }],
                [{ type: 'string', text: 'foobar\'' }],
            ])
    })

    it("tokenizes multiline comment", function () {
        var tokenizer = new Tokenizer()

        expect(tokenizer.tokenizeString("/* foo\nbar */"))
            .toEqual([
                [{ type: 'comment', text: '/* foo' }],
                [{ type: 'comment', text: 'bar */' }],
            ])
    })

    it("tokenizes two comments comment", function () {
        var tokenizer = new Tokenizer()

        expect(tokenizer.tokenizeString("/* foo *//* bar */")[0])
            .toEqual([
                { type: 'comment', text: '/* foo */' },
                { type: 'comment', text: '/* bar */' },
            ])
    })
})
<|endoftext|>


<commit_before>var httpProxy = require('../lib/http-proxy'),
    expect    = require('expect.js'),
    http      = require('http'),
    ws        = require('ws')
    io        = require('socket.io'),
    ioClient  = require('socket.io-client');

//
// Expose a port number generator.
// thanks to @3rd-Eden
//
var initialPort = 1024, gen = {};
Object.defineProperty(gen, 'port', {
  get: function get() {
    return initialPort++;
  }
});

describe('lib/http-proxy.js', function() {
  describe('#createProxyServer', function() {
    it.skip('should throw without options', function() {
      var error;
      try {
        httpProxy.createProxyServer();
      } catch(e) {
        error = e;
      }

      expect(error).to.be.an(Error);
    })

    it('should return an object otherwise', function() {
      var obj = httpProxy.createProxyServer({
        target: 'http://www.google.com:80'
      });

      expect(obj.web).to.be.a(Function);
      expect(obj.ws).to.be.a(Function);
      expect(obj.listen).to.be.a(Function);
    });
  });

  describe('#createProxyServer with forward options and using web-incoming passes', function () {
    it('should pipe the request using web-incoming#stream method', function (done) {
      var ports = { source: gen.port, proxy: gen.port };
      var proxy = httpProxy.createProxyServer({
        forward: 'http://127.0.0.1:' + ports.source
      }).listen(ports.proxy);

      var source = http.createServer(function(req, res) {
        expect(req.method).to.eql('GET');
        expect(req.headers.host.split(':')[1]).to.eql(ports.proxy);
        source.close();
        proxy._server.close();
        done();
      });

      source.listen(ports.source);
      http.request('http://127.0.0.1:' + ports.proxy, function() {}).end();
    })
  });

  describe('#createProxyServer using the web-incoming passes', function () {
    it('should make the request on pipe and finish it', function(done) {
      var ports = { source: gen.port, proxy: gen.port };
      var proxy = httpProxy.createProxyServer({
        target: 'http://127.0.0.1:' + ports.source
      }).listen(ports.proxy);

      var source = http.createServer(function(req, res) {
        expect(req.method).to.eql('POST');
        expect(req.headers['x-forwarded-for']).to.eql('127.0.0.1');
        expect(req.headers.host.split(':')[1]).to.eql(ports.proxy);
        source.close();
        proxy._server.close();
        done();
      });

      source.listen(ports.source);

      http.request({
        hostname: '127.0.0.1',
        port: ports.proxy,
        method: 'POST',
        headers: {
          'x-forwarded-for': '127.0.0.1'
        }
      }, function() {}).end();
    });
  });

  describe('#createProxyServer using the web-incoming passes', function () {
    it('should make the request, handle response and finish it', function(done) {
      var ports = { source: gen.port, proxy: gen.port };
      var proxy = httpProxy.createProxyServer({
        target: 'http://127.0.0.1:' + ports.source
      }).listen(ports.proxy);

      var source = http.createServer(function(req, res) {
        expect(req.method).to.eql('GET');
        expect(req.headers.host.split(':')[1]).to.eql(ports.proxy);
        res.writeHead(200, {'Content-Type': 'text/plain'})
        res.end('Hello from ' + source.address().port);
      });

      source.listen(ports.source);

      http.request({
        hostname: '127.0.0.1',
        port: ports.proxy,
        method: 'GET'
      }, function(res) {
        expect(res.statusCode).to.eql(200);

        res.on('data', function (data) {
          expect(data.toString()).to.eql('Hello from ' + ports.source);
        });

        res.on('end', function () {
          source.close();
          proxy._server.close();
          done();
        });
      }).end();
    });
  });

  describe('#createProxyServer() method with error response', function () {
    it('should make the request and emit the error event', function(done) {
      var ports = { source: gen.port, proxy: gen.port };
      var proxy = httpProxy.createProxyServer({
        target: 'http://127.0.0.1:' + ports.source
      });

      proxy.on('error', function (err) {
        expect(err).to.be.an(Error);
        expect(err.code).to.be('ECONNREFUSED');
        proxy._server.close();
        done();
      })

      proxy.listen(ports.proxy);

      http.request({
        hostname: '127.0.0.1',
        port: ports.proxy,
        method: 'GET',
      }, function() {}).end();
    });
  });

  describe('#createProxyServer setting the correct timeout value', function () {
    it('should hang up the socket at the timeout', function (done) {
      this.timeout(30);
      var ports = { source: gen.port, proxy: gen.port };
      var proxy = httpProxy.createProxyServer({
        target: 'http://127.0.0.1:' + ports.source,
        timeout: 3
      }).listen(ports.proxy);

      proxy.on('error', function (e) {
        expect(e).to.be.an(Error);
        expect(e.code).to.be.eql('ECONNRESET');
      });

      var source = http.createServer(function(req, res) {
        setTimeout(function () {
          res.end('At this point the socket should be closed');
        }, 5)
      });

      source.listen(ports.source);

      var testReq = http.request({
        hostname: '127.0.0.1',
        port: ports.proxy,
        method: 'GET',
      }, function() {});

      testReq.on('error', function (e) {
        expect(e).to.be.an(Error);
        expect(e.code).to.be.eql('ECONNRESET');
        proxy._server.close();
        source.close();
        done();
      });

      testReq.end();
    })
  })

  // describe('#createProxyServer using the web-incoming passes', function () {
  //   it('should emit events correclty', function(done) {
  //     var proxy = httpProxy.createProxyServer({
  //       target: 'http://127.0.0.1:8080'
  //     }),

  //     proxyServer = proxy.listen('8081'),

  //     source = http.createServer(function(req, res) {
  //       expect(req.method).to.eql('GET');
  //       expect(req.headers.host.split(':')[1]).to.eql('8081');
  //       res.writeHead(200, {'Content-Type': 'text/plain'})
  //       res.end('Hello from ' + source.address().port);
  //     }),

  //     events = [];

  //     source.listen('8080');

  //     proxy.ee.on('http-proxy:**', function (uno, dos, tres) {
  //       events.push(this.event);
  //     })

  //     http.request({
  //       hostname: '127.0.0.1',
  //       port: '8081',
  //       method: 'GET',
  //     }, function(res) {
  //       expect(res.statusCode).to.eql(200);

  //       res.on('data', function (data) {
  //         expect(data.toString()).to.eql('Hello from 8080');
  //       });

  //       res.on('end', function () {
  //         expect(events).to.contain('http-proxy:outgoing:web:begin');
  //         expect(events).to.contain('http-proxy:outgoing:web:end');
  //         source.close();
  //         proxyServer._server.close();
  //         done();
  //       });
  //     }).end();
  //   });
  // });

  describe('#createProxyServer using the ws-incoming passes', function () {
    it('should proxy the websockets stream', function (done) {
      var ports = { source: gen.port, proxy: gen.port };
      var proxy = httpProxy.createProxyServer({
        target: 'ws://127.0.0.1:' + ports.source,
        ws: true
      }),
      proxyServer = proxy.listen(ports.proxy),
      destiny = new ws.Server({ port: ports.source }, function () {
        var client = new ws('ws://127.0.0.1:' + ports.proxy);

        client.on('open', function () {
          client.send('hello there');
        });

        client.on('message', function (msg) {
          expect(msg).to.be('Hello over websockets');
          client.close();
          proxyServer._server.close();
          destiny.close();
          done();
        });
      });

      destiny.on('connection', function (socket) {
        socket.on('message', function (msg) {
          expect(msg).to.be('hello there');
          socket.send('Hello over websockets');
        });
      });
    });

    it('should emit error on proxy error', function (done) {
      var ports = { source: gen.port, proxy: gen.port };
      var proxy = httpProxy.createProxyServer({
        // note: we don't ever listen on this port
        target: 'ws://127.0.0.1:' + ports.source,
        ws: true
      }),
      proxyServer = proxy.listen(ports.proxy),
      client = new ws('ws://127.0.0.1:' + ports.proxy);

      client.on('open', function () {
        client.send('hello there');
      });

      proxy.on('error', function (err) {
        expect(err).to.be.an(Error);
        expect(err.code).to.be('ECONNREFUSED');
        proxyServer._server.close();
        done();
      });
    });

    it('should proxy a socket.io stream', function (done) {
      var ports = { source: gen.port, proxy: gen.port };
      var proxy = httpProxy.createProxyServer({
        target: 'ws://127.0.0.1:' + ports.source,
        ws: true
      }),
      proxyServer = proxy.listen(ports.proxy),
      destiny = io.listen(ports.source, function () {
        var client = ioClient.connect('ws://127.0.0.1:' + ports.proxy);

        client.on('connect', function () {
          client.emit('incoming', 'hello there');
        });

        client.on('outgoing', function (data) {
          expect(data).to.be('Hello over websockets');
          proxyServer._server.close();
          destiny.server.close();
          done();
        });
      });

      destiny.sockets.on('connection', function (socket) {
        socket.on('incoming', function (msg) {
          expect(msg).to.be('hello there');
          socket.emit('outgoing', 'Hello over websockets');
        });
      })
    });
  })
});
<commit_msg>Fix #657<commit_after>var httpProxy = require('../lib/http-proxy'),
    expect    = require('expect.js'),
    http      = require('http'),
    ws        = require('ws')
    io        = require('socket.io'),
    ioClient  = require('socket.io-client');

//
// Expose a port number generator.
// thanks to @3rd-Eden
//
var initialPort = 1024, gen = {};
Object.defineProperty(gen, 'port', {
  get: function get() {
    return initialPort++;
  }
});

describe('lib/http-proxy.js', function() {
  describe('#createProxyServer', function() {
    it.skip('should throw without options', function() {
      var error;
      try {
        httpProxy.createProxyServer();
      } catch(e) {
        error = e;
      }

      expect(error).to.be.an(Error);
    })

    it('should return an object otherwise', function() {
      var obj = httpProxy.createProxyServer({
        target: 'http://www.google.com:80'
      });

      expect(obj.web).to.be.a(Function);
      expect(obj.ws).to.be.a(Function);
      expect(obj.listen).to.be.a(Function);
    });
  });

  describe('#createProxyServer with forward options and using web-incoming passes', function () {
    it('should pipe the request using web-incoming#stream method', function (done) {
      var ports = { source: gen.port, proxy: gen.port };
      var proxy = httpProxy.createProxyServer({
        forward: 'http://127.0.0.1:' + ports.source
      }).listen(ports.proxy);

      var source = http.createServer(function(req, res) {
        expect(req.method).to.eql('GET');
        expect(req.headers.host.split(':')[1]).to.eql(ports.proxy);
        source.close();
        proxy._server.close();
        done();
      });

      source.listen(ports.source);
      http.request('http://127.0.0.1:' + ports.proxy, function() {}).end();
    })
  });

  describe('#createProxyServer using the web-incoming passes', function () {
    it('should make the request on pipe and finish it', function(done) {
      var ports = { source: gen.port, proxy: gen.port };
      var proxy = httpProxy.createProxyServer({
        target: 'http://127.0.0.1:' + ports.source
      }).listen(ports.proxy);

      var source = http.createServer(function(req, res) {
        expect(req.method).to.eql('POST');
        expect(req.headers['x-forwarded-for']).to.eql('127.0.0.1');
        expect(req.headers.host.split(':')[1]).to.eql(ports.proxy);
        source.close();
        proxy._server.close();
        done();
      });

      source.listen(ports.source);

      http.request({
        hostname: '127.0.0.1',
        port: ports.proxy,
        method: 'POST',
        headers: {
          'x-forwarded-for': '127.0.0.1'
        }
      }, function() {}).end();
    });
  });

  describe('#createProxyServer using the web-incoming passes', function () {
    it('should make the request, handle response and finish it', function(done) {
      var ports = { source: gen.port, proxy: gen.port };
      var proxy = httpProxy.createProxyServer({
        target: 'http://127.0.0.1:' + ports.source
      }).listen(ports.proxy);

      var source = http.createServer(function(req, res) {
        expect(req.method).to.eql('GET');
        expect(req.headers.host.split(':')[1]).to.eql(ports.proxy);
        res.writeHead(200, {'Content-Type': 'text/plain'})
        res.end('Hello from ' + source.address().port);
      });

      source.listen(ports.source);

      http.request({
        hostname: '127.0.0.1',
        port: ports.proxy,
        method: 'GET'
      }, function(res) {
        expect(res.statusCode).to.eql(200);

        res.on('data', function (data) {
          expect(data.toString()).to.eql('Hello from ' + ports.source);
        });

        res.on('end', function () {
          source.close();
          proxy._server.close();
          done();
        });
      }).end();
    });
  });

  describe('#createProxyServer() method with error response', function () {
    it('should make the request and emit the error event', function(done) {
      var ports = { source: gen.port, proxy: gen.port };
      var proxy = httpProxy.createProxyServer({
        target: 'http://127.0.0.1:' + ports.source
      });

      proxy.on('error', function (err) {
        expect(err).to.be.an(Error);
        expect(err.code).to.be('ECONNREFUSED');
        proxy._server.close();
        done();
      })

      proxy.listen(ports.proxy);

      http.request({
        hostname: '127.0.0.1',
        port: ports.proxy,
        method: 'GET',
      }, function() {}).end();
    });
  });

  describe('#createProxyServer setting the correct timeout value', function () {
    it('should hang up the socket at the timeout', function (done) {
      this.timeout(30);
      var ports = { source: gen.port, proxy: gen.port };
      var proxy = httpProxy.createProxyServer({
        target: 'http://127.0.0.1:' + ports.source,
        timeout: 3
      }).listen(ports.proxy);

      proxy.on('error', function (e) {
        expect(e).to.be.an(Error);
        expect(e.code).to.be.eql('ECONNRESET');
      });

      var source = http.createServer(function(req, res) {
        setTimeout(function () {
          res.end('At this point the socket should be closed');
        }, 5)
      });

      source.listen(ports.source);

      var testReq = http.request({
        hostname: '127.0.0.1',
        port: ports.proxy,
        method: 'GET',
      }, function() {});

      testReq.on('error', function (e) {
        expect(e).to.be.an(Error);
        expect(e.code).to.be.eql('ECONNRESET');
        proxy._server.close();
        source.close();
        done();
      });

      testReq.end();
    })
  })

  // describe('#createProxyServer using the web-incoming passes', function () {
  //   it('should emit events correclty', function(done) {
  //     var proxy = httpProxy.createProxyServer({
  //       target: 'http://127.0.0.1:8080'
  //     }),

  //     proxyServer = proxy.listen('8081'),

  //     source = http.createServer(function(req, res) {
  //       expect(req.method).to.eql('GET');
  //       expect(req.headers.host.split(':')[1]).to.eql('8081');
  //       res.writeHead(200, {'Content-Type': 'text/plain'})
  //       res.end('Hello from ' + source.address().port);
  //     }),

  //     events = [];

  //     source.listen('8080');

  //     proxy.ee.on('http-proxy:**', function (uno, dos, tres) {
  //       events.push(this.event);
  //     })

  //     http.request({
  //       hostname: '127.0.0.1',
  //       port: '8081',
  //       method: 'GET',
  //     }, function(res) {
  //       expect(res.statusCode).to.eql(200);

  //       res.on('data', function (data) {
  //         expect(data.toString()).to.eql('Hello from 8080');
  //       });

  //       res.on('end', function () {
  //         expect(events).to.contain('http-proxy:outgoing:web:begin');
  //         expect(events).to.contain('http-proxy:outgoing:web:end');
  //         source.close();
  //         proxyServer._server.close();
  //         done();
  //       });
  //     }).end();
  //   });
  // });

  describe('#createProxyServer using the ws-incoming passes', function () {
    it('should proxy the websockets stream', function (done) {
      var ports = { source: gen.port, proxy: gen.port };
      var proxy = httpProxy.createProxyServer({
        target: 'ws://127.0.0.1:' + ports.source,
        ws: true
      }),
      proxyServer = proxy.listen(ports.proxy),
      destiny = new ws.Server({ port: ports.source }, function () {
        var client = new ws('ws://127.0.0.1:' + ports.proxy);

        client.on('open', function () {
          client.send('hello there');
        });

        client.on('message', function (msg) {
          expect(msg).to.be('Hello over websockets');
          client.close();
          proxyServer._server.close();
          destiny.close();
          done();
        });
      });

      destiny.on('connection', function (socket) {
        socket.on('message', function (msg) {
          expect(msg).to.be('hello there');
          socket.send('Hello over websockets');
        });
      });
    });

    it('should emit error on proxy error', function (done) {
      var ports = { source: gen.port, proxy: gen.port };
      var proxy = httpProxy.createProxyServer({
        // note: we don't ever listen on this port
        target: 'ws://127.0.0.1:' + ports.source,
        ws: true
      }),
      proxyServer = proxy.listen(ports.proxy),
      client = new ws('ws://127.0.0.1:' + ports.proxy);

      client.on('open', function () {
        client.send('hello there');
      });

      proxy.on('error', function (err) {
        expect(err).to.be.an(Error);
        expect(err.code).to.be('ECONNREFUSED');
        proxyServer._server.close();
        done();
      });
    });

    it('should proxy a socket.io stream', function (done) {
      var ports = { source: gen.port, proxy: gen.port };
      var proxy = httpProxy.createProxyServer({
        target: 'ws://127.0.0.1:' + ports.source,
        ws: true
      });
      proxyServer = proxy.listen(ports.proxy);
      var server = http.createServer();
      destiny = io.listen(server);
      function startSocketIo() {
        var client = ioClient.connect('ws://127.0.0.1:' + ports.proxy);

        client.on('connect', function () {
          client.emit('incoming', 'hello there');
        });

        client.on('outgoing', function (data) {
          expect(data).to.be('Hello over websockets');
          proxyServer._server.close();
          server.close();
          done();
        });
      }
      server.listen(ports.source);
      server.on('listening', startSocketIo);

      destiny.sockets.on('connection', function (socket) {
        socket.on('incoming', function (msg) {
          expect(msg).to.be('hello there');
          socket.emit('outgoing', 'Hello over websockets');
        });
      })
    });
  })
});
<|endoftext|>


<commit_before>/*@preserve Copyright (C) 2016 Crawford Currie http://c-dot.co.uk license MIT*/

/*eslint-env node */

const Q = require("q");
const Fs = require("fs");
const readFile = Q.denodeify(Fs.readFile);

const Utils = require("../common/Utils.js");
const Time = require("../common/Time.js");

const CACHE_LENGTH = 24 * 60 * 60 * 1000; // cache size in ms

const TAG = "Calendar";

/**
 * Calendar event cache entry. Events stored here are filtered; only central
 * heating events are cached. The cache size is limited by CACHE_LENGTH.
 * Longer means less frequent automatic updates, and larger memory
 * footprint for the server, but less network traffic.
 * @ignore
 */
function ScheduledEvent(cal, id, start, end, pin, state) {
    var now = Time.now();
    this.calendar = cal;
    this.id = id;
    this.pin = pin;
    this.state = state;
    this.startms = start;
    this.endms = end;

    var self = this;
    if (start > now) {
        Utils.TRACE(TAG, "Event will start at ", Date, start,
                   " now is ", Date, now);
        this.event = setTimeout(function() {
            self.start();
        }, start - now);
    } else if (start <= now && end > now) {
        Utils.TRACE(TAG, "Event has already started");
        this.start();
    } else {
        Utils.TRACE(TAG, "Event is already finished");
    }
}

ScheduledEvent.prototype.cancel = function() {
    Utils.TRACE(TAG, this.id, " cancelled");
    if (typeof this.event !== "undefined")
        clearTimeout(this.event);
    if (typeof this.calendar.remove === "function")
        this.calendar.remove(this.id, this.pin);
    this.event = undefined;
};

ScheduledEvent.prototype.start = function() {
    Utils.TRACE(TAG, this.id, " starting");
    if (typeof this.calendar.trigger === "function")
        this.calendar.trigger(this.id, this.pin, this.state, this.endms);
    var self = this;
    this.event = setTimeout(function() {
        self.calendar.remove(self.id, self.pin);
    }, this.endms - Time.now());
};

/**
 * Get active events from a Google calendar.
 * @param {string} name name of the calendar
 * @param {Config} config configuration
 * * `id`: calendar id, as used by Google
 * * `auth_cache`: file to cache authorisation in
 * * `secrets`: secrets used by google OAuth
 *   * `client_id`
 *   * `client_secret`
 *   * `redirect_uris`
 * @param {function} trigger callback triggered when an event starts
 * (or after an update and the event has already started).
 * ```
 * trigger(String id, String pin, int state, int until)
 * ```
 * * `id` id of the event
 * * `pin` in the even is for (or `ALL` for all pins)
 * * `state` required state 0|1|2
 * * `until` when the event ends (epoch ms)
 * @param {function} remove callback invoked when a scheduled event is removed.
 * ```
 * remove(String id, String pin)
 * ```
 * * `id` id of the event being removed
 * * `pin` pin the event appies to
 * @class
 */
function Calendar(name, config, trigger, remove) {
    "use strict";
    this.name = name;
    this.config = config;
    this.oauth2Client = undefined;
    this.schedule = [];
    this.trigger = trigger;
    this.remove = remove;
}
module.exports = Calendar;

/**
 * Return a promise to start the calendar
 * @private
 */
Calendar.prototype.authorise = function() {
    "use strict";
    if (typeof this.oauth2Client !== "undefined")
        return Q(); // already started

    var self = this;

    return readFile(Utils.expandEnvVars(self.config.auth_cache))

    .then(function(token) {
        var clientSecret = self.config.secrets.client_secret;
        var clientId = self.config.secrets.client_id;
        var redirectUrl = self.config.secrets.redirect_uris[0];
        var googleAuth = require("google-auth-library");
        var auth = new googleAuth();
        self.oauth2Client = new auth.OAuth2(
            clientId, clientSecret, redirectUrl);
        self.oauth2Client.credentials = JSON.parse(token);
    });
};

/**
 * Return a promise that will update the list of the events
 * stored for the next 24 hours.
 * @private
 */
Calendar.prototype.fillCache = function() {
    "use strict";
    var self = this;

    return this.authorise()

    .then(function() {
        var google = require("googleapis");
        var calendar = google.calendar("v3");
        var now = Time.now();

        // Q.denodeify doesn't work for this, so have to promisify it
        // manually :-(
        return Q.Promise(function(ok, fail) {
            calendar.events.list(
                {
                    auth: self.oauth2Client,
                    calendarId: "primary",//self.config.id,
                    // For reasons undocumented by google, if timeMin and
                    // timeMax are the same time it returns no events. So
                    // we need to offset them by a second.
                    timeMin: (new Date()).toISOString(),
                    timeMax: (new Date(now + CACHE_LENGTH))
                              .toISOString(),
                    q: "Hotpot:",
                    singleEvents: true
                },
                function(err, response) {
                    if (err) {
                        Utils.TRACE(TAG, self.name, " update failed ", err);
                        fail(err);
                    } else {
                        ok(response);
                    }
                });
        });
    })

    .then(function(response) {
        self.clearSchedule();
        var events = response.items;
        for (var i = 0; i < events.length; i++) {
            var event = events[i];
            var start = Date.parse(event.start.dateTime || event.start.date);
            var end = Date.parse(event.end.dateTime || event.end.date);
            // Can have orders in the event summary or the description
            var fullText = event.summary + " " + event.description;
            var re = /HOTPOT\s*:\s*([A-Z]+)\s+([A-Z0-9]+)/ig;
            var match;
            while ((match = re.exec(fullText)) !== null) {
                var pin = match[1];
                var state = match[2];
                if (/^[0-9]+$/i.test(state))
                    state = parseInt(state);
                else if (/^(off|away)$/i.test(state))
                    state = 0;
                else if (/^on$/i.test(state))
                    state = 1;
                else if (/^boost$/i.test(state))
                    state = 2;
                else {
                    Utils.TRACE(TAG, "Ignored bad calendar entry ",
                                Date, start, "..",
                                Date, end, " ",
                                pin, "=", state);
                    continue;
                }
                Utils.TRACE(TAG, "Got entry ", Date, start, "..",
                            Date, end, " ",
                            pin, "=", state);
                self.schedule.push(new ScheduledEvent(
                    self,
                    "Calendar:" + self.name + ":" + self.schedule.length,
                    start, end, pin, state));
            }
        }
        Utils.TRACE(TAG, self.name, " ready");
    })

    .catch(function(e) {
        throw "Calendar had an error: " + e.stack;
    });
};

/**
 * Clear the existing schedule
 * @private
 */
Calendar.prototype.clearSchedule = function() {
    for (var i in this.schedule)
        this.schedule[i].cancel();
    this.schedule = [];
};

/**
 * Schedule a calendar update in 'after' milliseconds. The update is
 * performed asynchronously.
 * @param {Number} after delay before updating the calendar asynchronously
 * @private
 */
Calendar.prototype.update = function(after) {
    "use strict";
    var self = this;

    // Kill the old timer
    if (this.update_timeout)
        clearTimeout(this.update_timeout);
    this.update_timeout = setTimeout(function() {
        Utils.TRACE(TAG, "Updating '", self.name, "'");
        self.fillCache().done(function() {
            self.update_timeout = undefined;
            self.update(CACHE_LENGTH);
        });
    }, after);
};
<commit_msg>Allow = in calendars<commit_after>/*@preserve Copyright (C) 2016 Crawford Currie http://c-dot.co.uk license MIT*/

/*eslint-env node */

const Q = require("q");
const Fs = require("fs");
const readFile = Q.denodeify(Fs.readFile);

const Utils = require("../common/Utils.js");
const Time = require("../common/Time.js");

const CACHE_LENGTH = 24 * 60 * 60 * 1000; // cache size in ms

const TAG = "Calendar";

/**
 * Calendar event cache entry. Events stored here are filtered; only central
 * heating events are cached. The cache size is limited by CACHE_LENGTH.
 * Longer means less frequent automatic updates, and larger memory
 * footprint for the server, but less network traffic.
 * @ignore
 */
function ScheduledEvent(cal, id, start, end, pin, state) {
    var now = Time.now();
    this.calendar = cal;
    this.id = id;
    this.pin = pin;
    this.state = state;
    this.startms = start;
    this.endms = end;

    var self = this;
    if (start > now) {
        Utils.TRACE(TAG, "Event will start at ", Date, start,
                   " now is ", Date, now);
        this.event = setTimeout(function() {
            self.start();
        }, start - now);
    } else if (start <= now && end > now) {
        Utils.TRACE(TAG, "Event has already started");
        this.start();
    } else {
        Utils.TRACE(TAG, "Event is already finished");
    }
}

ScheduledEvent.prototype.cancel = function() {
    Utils.TRACE(TAG, this.id, " cancelled");
    if (typeof this.event !== "undefined")
        clearTimeout(this.event);
    if (typeof this.calendar.remove === "function")
        this.calendar.remove(this.id, this.pin);
    this.event = undefined;
};

ScheduledEvent.prototype.start = function() {
    Utils.TRACE(TAG, this.id, " starting");
    if (typeof this.calendar.trigger === "function")
        this.calendar.trigger(this.id, this.pin, this.state, this.endms);
    var self = this;
    this.event = setTimeout(function() {
        self.calendar.remove(self.id, self.pin);
    }, this.endms - Time.now());
};

/**
 * Get active events from a Google calendar.
 * @param {string} name name of the calendar
 * @param {Config} config configuration
 * * `id`: calendar id, as used by Google
 * * `auth_cache`: file to cache authorisation in
 * * `secrets`: secrets used by google OAuth
 *   * `client_id`
 *   * `client_secret`
 *   * `redirect_uris`
 * @param {function} trigger callback triggered when an event starts
 * (or after an update and the event has already started).
 * ```
 * trigger(String id, String pin, int state, int until)
 * ```
 * * `id` id of the event
 * * `pin` in the even is for (or `ALL` for all pins)
 * * `state` required state 0|1|2
 * * `until` when the event ends (epoch ms)
 * @param {function} remove callback invoked when a scheduled event is removed.
 * ```
 * remove(String id, String pin)
 * ```
 * * `id` id of the event being removed
 * * `pin` pin the event appies to
 * @class
 */
function Calendar(name, config, trigger, remove) {
    "use strict";
    this.name = name;
    this.config = config;
    this.oauth2Client = undefined;
    this.schedule = [];
    this.trigger = trigger;
    this.remove = remove;
}
module.exports = Calendar;

/**
 * Return a promise to start the calendar
 * @private
 */
Calendar.prototype.authorise = function() {
    "use strict";
    if (typeof this.oauth2Client !== "undefined")
        return Q(); // already started

    var self = this;

    return readFile(Utils.expandEnvVars(self.config.auth_cache))

    .then(function(token) {
        var clientSecret = self.config.secrets.client_secret;
        var clientId = self.config.secrets.client_id;
        var redirectUrl = self.config.secrets.redirect_uris[0];
        var googleAuth = require("google-auth-library");
        var auth = new googleAuth();
        self.oauth2Client = new auth.OAuth2(
            clientId, clientSecret, redirectUrl);
        self.oauth2Client.credentials = JSON.parse(token);
    });
};

/**
 * Return a promise that will update the list of the events
 * stored for the next 24 hours.
 * @private
 */
Calendar.prototype.fillCache = function() {
    "use strict";
    var self = this;

    return this.authorise()

    .then(function() {
        var google = require("googleapis");
        var calendar = google.calendar("v3");
        var now = Time.now();

        // Q.denodeify doesn't work for this, so have to promisify it
        // manually :-(
        return Q.Promise(function(ok, fail) {
            calendar.events.list(
                {
                    auth: self.oauth2Client,
                    calendarId: "primary",//self.config.id,
                    // For reasons undocumented by google, if timeMin and
                    // timeMax are the same time it returns no events. So
                    // we need to offset them by a second.
                    timeMin: (new Date()).toISOString(),
                    timeMax: (new Date(now + CACHE_LENGTH))
                              .toISOString(),
                    q: "Hotpot:",
                    singleEvents: true
                },
                function(err, response) {
                    if (err) {
                        Utils.TRACE(TAG, self.name, " update failed ", err);
                        fail(err);
                    } else {
                        ok(response);
                    }
                });
        });
    })

    .then(function(response) {
        self.clearSchedule();
        var events = response.items;
        for (var i = 0; i < events.length; i++) {
            var event = events[i];
            var start = Date.parse(event.start.dateTime || event.start.date);
            var end = Date.parse(event.end.dateTime || event.end.date);
            // Can have orders in the event summary or the description
            var fullText = event.summary + " " + event.description;
            var re = /HOTPOT\s*:\s*([A-Z]+)[=\s]+([A-Z0-9]+)/ig;
            var match;
            while ((match = re.exec(fullText)) !== null) {
                var pin = match[1];
                var state = match[2];
                if (/^[0-9]+$/i.test(state))
                    state = parseInt(state);
                else if (/^(off|away)$/i.test(state))
                    state = 0;
                else if (/^on$/i.test(state))
                    state = 1;
                else if (/^boost$/i.test(state))
                    state = 2;
                else {
                    Utils.TRACE(TAG, "Ignored bad calendar entry ",
                                Date, start, "..",
                                Date, end, " ",
                                pin, "=", state);
                    continue;
                }
                Utils.TRACE(TAG, "Got entry ", Date, start, "..",
                            Date, end, " ",
                            pin, "=", state);
                self.schedule.push(new ScheduledEvent(
                    self,
                    "Calendar:" + self.name + ":" + self.schedule.length,
                    start, end, pin, state));
            }
        }
        Utils.TRACE(TAG, self.name, " ready");
    })

    .catch(function(e) {
        throw "Calendar had an error: " + e.stack;
    });
};

/**
 * Clear the existing schedule
 * @private
 */
Calendar.prototype.clearSchedule = function() {
    for (var i in this.schedule)
        this.schedule[i].cancel();
    this.schedule = [];
};

/**
 * Schedule a calendar update in 'after' milliseconds. The update is
 * performed asynchronously.
 * @param {Number} after delay before updating the calendar asynchronously
 * @private
 */
Calendar.prototype.update = function(after) {
    "use strict";
    var self = this;

    // Kill the old timer
    if (this.update_timeout)
        clearTimeout(this.update_timeout);
    this.update_timeout = setTimeout(function() {
        Utils.TRACE(TAG, "Updating '", self.name, "'");
        self.fillCache().done(function() {
            self.update_timeout = undefined;
            self.update(CACHE_LENGTH);
        });
    }, after);
};
<|endoftext|>


<commit_before>
const { inSubarea } = require('../../helpers/geometry');
const { sendHint, sendHunt, sendSimpleLocation } = require('../telegram/index');

const express = require('express');
const models = require('../models');
const geocoder = require('geocoder');
const config = require('./../../config');
const router = express.Router();
const rdToWgs = require('rdtowgs');
const wgstoord = require('./../wgstord');

geocoder.selectProvider('google', { appid: config.google.googleAppId });
const checkJwt = require('./../checkJwt');

router.get('/', checkJwt, (req, res) => {
  models.Hint.findAll({
    include: [models.HintType, models.User, models.Subarea],
  }).then((hints) => {
    res.send(hints);
  });
});

/**
 * Given an WGS-coordinate, calculate RD, subarea and retrieve the street address.
 */
router.post('/location', checkJwt, (req, res) => {
  if (req.body.latlng) {
    const rd = null;
    const subarea = inSubarea(req.body.latlng);
    geocoder.reverseGeocode(req.body.latlng[0], req.body.latlng[1], (err, data) => {
      res.send({
        rd,
        subarea,
        address: data,
      });
    });
  } else {
    res.sendStatus(400);
  }
});

/**
 * Given an RD-coordinate, calculate WSG-84, subarea and retrieve the street address.
 */
router.post('/information', checkJwt, (req, res) => {
  // Convert RD-coordinate to WSG-84
  const wgs = rdToWgs(req.body.longitude, req.body.latitude);
  const subarea = inSubarea(wgs);
  geocoder.reverseGeocode(wgs[0], wgs[1], (err, data) => {
    res.send({
      wgs,
      subarea,
      address: data,
    });
  });
});

/**
 * Save Clairvoyance Hints to database and send a Telegram-chat.
 */
router.post('/clairvoyance', checkJwt, (req, res) => {
  if (req.body.data) {
    req.body.data.map((entry) => {
      const hint = entry.split(' ');
      const wgs = rdToWgs(hint[0], hint[1]);
      const hintSubarea = inSubarea(wgs);
      geocoder.reverseGeocode(wgs[0], wgs[1], (err, data) => {
        models.Subarea.findAll({
          where: {
            name: hintSubarea,
          },
        }).then((subareas) => {
          subareas.map((subarea) => {
            models.Hint.create({
              latitude: wgs[0],
              longitude: wgs[1],
              rdx: hint[1],
              rdy: hint[0],
              address: data.results[0] ? data.results[0].formatted_address : null,
              SubareaId: subarea.id,
              HintTypeId: 1,
              UserId: 1,
            });
            sendHint(hintSubarea, wgs[0], wgs[1], data.results[0] ? data.results[0].formatted_address : null);
            return null;
          });
        });
      });
      return null;
    });
  }
  res.send({});
});

/**
 * Save hint to the database and send a Telegram-chat.
 */
router.post('/', checkJwt, (req, res) => {
  models.Subarea.findAll({
    where: {
      name: req.body.subarea,
    },
  }).then((subareas) => {
    subareas.map((subarea) => {
      const rd = wgstoord(req.body.latitude, req.body.longitude);

      models.Hint.create({
        latitude: req.body.latitude,
        longitude: req.body.longitude,
        rdx: req.body.rdx || rd[1],
        rdy: req.body.rdy || rd[0],
        address: req.body.address,
        SubareaId: subarea.id,
        HintTypeId: req.body.hintTypeId ? req.body.hintTypeId : 1,
        UserId: 1,
      });
      if (req.body.hintTypeId) {
        if (req.body.hintTypeId === 2) {
          sendHunt(req.body.subarea, req.body.latitude, req.body.longitude, req.body.address);
        } else {
          sendSimpleLocation(req.body.subarea, req.body.latitude, req.body.longitude, req.body.address);
        }
      } else {
        sendHint(req.body.subarea, req.body.latitude, req.body.longitude, req.body.address);
      }
      res.send({ message: 'success' });
    });
  });
});

router.get('/:type', checkJwt, (req, res) => {
  models.Hint.findAll({
    include: [{
      model: models.HintType,
      where: {
        name: req.params.type,
      },
    }, models.User, models.Subarea],
  }).then((hints) => {
    res.send(hints);
  });
});

router.get('/delete/:hintId', checkJwt, (req, res) => {
  models.Hint.destroy({
    where: {
      id: req.params.hintId,
    },
  }).then(() => {
    res.send({ message: 'success' });
  });
});

router.post('/', (req, res) =>
  res.send(200)
);

module.exports = router;
<commit_msg>Added correct usuage of key<commit_after>
const { inSubarea } = require('../../helpers/geometry');
const { sendHint, sendHunt, sendSimpleLocation } = require('../telegram/index');

const express = require('express');
const models = require('../models');
const geocoder = require('geocoder');
const config = require('./../../config');
const router = express.Router();
const rdToWgs = require('rdtowgs');
const wgstoord = require('./../wgstord');

geocoder.selectProvider('google', { key: config.google.googleAppId });
const checkJwt = require('./../checkJwt');

router.get('/', checkJwt, (req, res) => {
  models.Hint.findAll({
    include: [models.HintType, models.User, models.Subarea],
  }).then((hints) => {
    res.send(hints);
  });
});

/**
 * Given an WGS-coordinate, calculate RD, subarea and retrieve the street address.
 */
router.post('/location', checkJwt, (req, res) => {
  if (req.body.latlng) {
    const rd = null;
    const subarea = inSubarea(req.body.latlng);
    geocoder.reverseGeocode(req.body.latlng[0], req.body.latlng[1], (err, data) => {
      res.send({
        rd,
        subarea,
        address: data,
      });
    });
  } else {
    res.sendStatus(400);
  }
});

/**
 * Given an RD-coordinate, calculate WSG-84, subarea and retrieve the street address.
 */
router.post('/information', checkJwt, (req, res) => {
  // Convert RD-coordinate to WSG-84
  const wgs = rdToWgs(req.body.longitude, req.body.latitude);
  const subarea = inSubarea(wgs);
  geocoder.reverseGeocode(wgs[0], wgs[1], (err, data) => {
    res.send({
      wgs,
      subarea,
      address: data,
    });
  });
});

// {"wgs":[52.19973591706119,6.213069797742666],"subarea":"Bravo","address":{"error_message":"You have exceeded your daily request quota for this API. We recommend registering for a key at the Google Developers Console: https://console.developers.google.com/apis/credentials?project=_","results":[],"status":"OVER_QUERY_LIMIT"}}

/**
 * Save Clairvoyance Hints to database and send a Telegram-chat.
 */
router.post('/clairvoyance', checkJwt, (req, res) => {
  if (req.body.data) {
    req.body.data.map((entry) => {
      const hint = entry.split(' ');
      const wgs = rdToWgs(hint[0], hint[1]);
      const hintSubarea = inSubarea(wgs);
      geocoder.reverseGeocode(wgs[0], wgs[1], (err, data) => {
        models.Subarea.findAll({
          where: {
            name: hintSubarea,
          },
        }).then((subareas) => {
          subareas.map((subarea) => {
            models.Hint.create({
              latitude: wgs[0],
              longitude: wgs[1],
              rdx: hint[1],
              rdy: hint[0],
              address: data.results[0] ? data.results[0].formatted_address : null,
              SubareaId: subarea.id,
              HintTypeId: 1,
              UserId: 1,
            });
            sendHint(hintSubarea, wgs[0], wgs[1], data.results[0] ? data.results[0].formatted_address : null);
            return null;
          });
        });
      });
      return null;
    });
  }
  res.send({});
});

/**
 * Save hint to the database and send a Telegram-chat.
 */
router.post('/', checkJwt, (req, res) => {
  models.Subarea.findAll({
    where: {
      name: req.body.subarea,
    },
  }).then((subareas) => {
    subareas.map((subarea) => {
      const rd = wgstoord(req.body.latitude, req.body.longitude);

      models.Hint.create({
        latitude: req.body.latitude,
        longitude: req.body.longitude,
        rdx: req.body.rdx || rd[1],
        rdy: req.body.rdy || rd[0],
        address: req.body.address,
        SubareaId: subarea.id,
        HintTypeId: req.body.hintTypeId ? req.body.hintTypeId : 1,
        UserId: 1,
      });
      if (req.body.hintTypeId) {
        if (req.body.hintTypeId === 2) {
          sendHunt(req.body.subarea, req.body.latitude, req.body.longitude, req.body.address);
        } else {
          sendSimpleLocation(req.body.subarea, req.body.latitude, req.body.longitude, req.body.address);
        }
      } else {
        sendHint(req.body.subarea, req.body.latitude, req.body.longitude, req.body.address);
      }
      res.send({ message: 'success' });
    });
  });
});

router.get('/:type', checkJwt, (req, res) => {
  models.Hint.findAll({
    include: [{
      model: models.HintType,
      where: {
        name: req.params.type,
      },
    }, models.User, models.Subarea],
  }).then((hints) => {
    res.send(hints);
  });
});

router.get('/delete/:hintId', checkJwt, (req, res) => {
  models.Hint.destroy({
    where: {
      id: req.params.hintId,
    },
  }).then(() => {
    res.send({ message: 'success' });
  });
});

router.post('/', (req, res) =>
  res.send(200)
);

module.exports = router;
<|endoftext|>