bonescriptjtjk
Version:
Physical computing library for embedded Linux
188 lines (166 loc) • 6 kB
JavaScript
// Copyright (C) 2011 - Texas Instruments, Jason Kridner
//
//
var b = require('../main');
var fs = require('fs');
var url = require('url');
var child_process = require('child_process');
var winston = require('winston');
var socketio = require('socket.io');
var debug = process.env.DEBUG ? true : false;
exports.socketJSReqHandler = function(req, res) {
function sendFile(err, file) {
if(err) {
res.writeHead(500, {"Content-Type": "text/plain"});
res.end(err + '\n');
return;
}
res.setHeader('Content-Type', 'text/javascript');
file = file.replace(/___INSERT_HOST___/g, host);
res.end(file);
}
var parsedUrl = url.parse(req.url);
var uri = parsedUrl.pathname;
var host = 'http://' + req.headers.host;
if(uri == '/bonescript.js') {
var filename = __dirname + '/bonescript.js';
if(debug) winston.debug('filename = ' + filename)
fs.readFile(filename, 'utf8', sendFile);
}
}
exports.addSocketListeners = function(server, serverEmitter) {
var io = socketio(server);
if(debug) winston.debug('Listening for new socket.io clients');
io.on('connection', onconnect);
function onconnect(socket) {
winston.debug('Client connected');
serverEmitter.emit('socket$connect', socket);
// on disconnect
socket.on('disconnect', function() {
if(debug) winston.debug('Client disconnected');
serverEmitter.emit('socket$disconnect');
});
socket.on('message', serverMessage);
spawn(socket);
var modmsg = {};
modmsg.module = 'bonescript';
modmsg.data = {};
var callMyFunc = function(name, m) {
var myCallback = function(resp) {
if(debug) winston.debug(name + ' replied to ' + JSON.stringify(m) + ' with ' + JSON.stringify(resp));
if(typeof m.seq == 'undefined') return;
if(!resp || (typeof resp != 'object')) resp = {'data': resp};
resp.seq = m.seq;
// TODO: consider setting 'oneshot'
if(debug) winston.debug('Sending message "bonescript": ' + JSON.stringify(resp));
socket.emit('bonescript', resp);
};
try {
var callargs = [];
for(var arg in b[name].args) {
var argname = b[name].args[arg];
if(argname == 'callback') {
if(typeof m.seq == 'number') callargs.push(myCallback);
else callargs.push(null);
} else if(typeof m[argname] != 'undefined') {
callargs.push(m[argname]);
} else {
callargs.push(undefined);
}
}
if(debug) winston.debug('Calling ' + name + '(' + callargs.join(',') + ')');
b[name].apply(this, callargs);
} catch(ex) {
if(debug) winston.debug('Error handing ' + name + ' message: ' + ex);
if(debug) winston.debug('m = ' + JSON.stringify(m));
}
};
var addSocketX = function(message, name) {
var onFuncMessage = function(m) {
callMyFunc(name, m);
};
socket.on(message, onFuncMessage);
};
var b = require('../main');
for(var i in b) {
if(typeof b[i] == 'function') {
if(typeof b[i].args != 'undefined') {
modmsg.data[i] = {};
modmsg.data[i].name = i;
modmsg.data[i].type = 'function';
modmsg.data[i].value = b[i].args;
addSocketX('bonescript$' + i, i);
}
} else {
modmsg.data[i] = {};
modmsg.data[i].name = i;
modmsg.data[i].type = typeof b[i];
modmsg.data[i].value = b[i];
}
}
socket.emit('require', modmsg);
}
function serverMessage(message) {
serverEmitter.emit('message', message);
}
return(io);
}
// most heavily borrowed from https://github.com/itchyny/browsershell
function spawn(socket) {
var stream = '';
var timer;
var len = 0;
var c;
socket.on('shell', receive);
return(receive);
function receive(msg) {
if(!c) {
try {
if(debug) winston.debug('Spawning bash');
c = child_process.spawn('/bin/bash', ['-i'], {customFds: [-1, -1, -1]});
c.stdout.on('data', send);
c.stderr.on('data', send);
c.on('exit', function() {
socket.emit('shell', send('\nexited\n'));
c = undefined;
});
socket.on('disconnect', function () {
if(debug) winston.debug('Killing bash');
c.kill('SIGHUP');
});
} catch(ex) {
c = undefined;
send('Error invoking bash');
winston.error('Error invoking bash');
}
}
if(c) {
if(msg) {
c.stdin.write(msg + '\n', 'utf-8');
}
} else {
winston.error('Unable to invoke child process');
}
}
function send(data) {
// add data to the stream
stream += data.toString();
++len;
// clear any existing timeout if it exists
if(timer) clearTimeout(timer);
// set new timeout
timer = setTimeout(function () {
socket.emit('shell', stream);
stream = '';
len = 0;
}, 100);
// send data if over threshold
if(len > 1000)
{
clearTimeout(timer);
socket.emit('shell', stream);
stream = '';
len = 0;
}
}
}