Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

V1.1 #4

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ language:
- objective-c
env:
matrix:
- NODE_VERSION="4.1.1"
- NODE_VERSION="6"
global:
- secure: BuCxvkJOa8k0NzsoiFDlJQjzj5+xcbnaA4+A8maNJDAe3etcBSZBA+4nxm18jABeuE9NNg5k2rayrGKhF6kqpy0TcgvtUOx38of0FP7zaO34kKwbPuKymvoLVJ58caQsQCbBGc/UTdBxHLTe8WN2G0wkRFmWg5YKNqg5rgyrNB5e8V1bWfILg/PRX5jla/59sG5+A/2vGa6HRpiEdRk0QvxUIJoHrh0DmkaOkQ3FK5wKd1vvfPfqQVLF6xiDjZ+C2203eqjcpapNz/6yX/C7X4XtSgNHcyOnWpcY2A9m+CYINzPfoAPnZJpdc5vWfDyR6rQJ/3PcG3Ol8M2eDwH2FjhaPpNwotrAHZUNod7WCaRaWVfb8Qku4fnO0LpQQ+eBIKbHCR4VeNWUo7EX7T3445oxxft7nwszaLpL1DYKFgoMAAbTclycJFUK4cZwYCSxOYKfdYKi9R5889TEw32FRCLrOjW3Pglqo2ljPsGk/pG8MmRzA2sI96xUFUwHuuUhmmbXd8oSCbWZjWaB3gxxVRKG26LfK8DAR6w0+7C42Z2JuNsdnQopTRxicbXCr13i5ZPOSrZrgEpwm3tHU3MCO5uXBlrpl4jwyBBqkQusJOiP1tc/5Iu008Y0cHty/Z5Juu1apaAm8NrXKkJt+jerewbIT4cSqYFzWRAfC8opQUc=
- secure: LbuFSJhaqH8X84n9D754UN0dqeTbHEFU1Z4TRvtSZfG9tRrMWqvf5vOaiV/6fAqgLVo1eL5CUcEvrDrLxVLJTXs8V4UPJT+H8mUPVFlX4fOLCYDHllphUUnyDH7rPSb5QkfPAIa0rl5YtHPpMi0HwYD75L5uEwZGJEaj1rE/BqjrXROoCakIRcgi2GzUA3WiPSQPxry2AEI948Kv+jotD990UjRYQDlx+RCdS3bzHaBHvKJPKvVNWaf+g29xNZI3vMu7bZZj25BeheOmta1mMfGduSEKjxc4BukHOaN7ydyjiVEMIvqx5es3VQNr0CfJpkZAS3Y+6r2GxZ4zmUdwEKH5nJZ5RmKvXt4L3mp+fs1W5IdOCmxihJtRvt+JjZGEDdgqx33t2uv+kZOdaFHpVzD8DdQZfcfzTgLS3TFSo/7QgbAlCq88kwV+OwHoIPiW07cqHHon4pyS5IdvX8L0wEymLXna+l2TM0eELGnttd4mFu1BBgWg2HLoQKohmCztcof1bw+63Ugi2nFdsHWPfOqvQGhyXBBHMcFdWO7a1fm3JaQvD99svl6vNyI58q7byHTYVoHcMTVGSTYA4bEe2s6l/GIJTiJP5y8d9uLz9ASq6OKUJ/8OCulFbzLzN/BkJ09r7kmVGJsNkyUyuqFtLBMsR0fzUPNahLsj8+v0Ns8=
Expand Down
167 changes: 103 additions & 64 deletions backend.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* A Node.JS back-end for Remote View feature of LiveStyle app:
* A Node.JS back-end for Remote View feature of LiveStyle app:
* manages connections to LiveStyle and Remote View servers and
* responds to RV messages.
*
Expand All @@ -8,122 +8,161 @@
*/
'use strict';

var debug = require('debug')('lsapp:backend');
var extend = require('xtend');
var tunnelController = require('./lib/controller/tunnel');
var fileServerController = require('./lib/controller/file-server');
const debug = require('debug')('lsapp:backend');
const TunnelController = require('./lib/controller/tunnel');
const fileServer = require('./lib/file-server');
const pkg = require('./package.json');

const tunnels = new TunnelController(pkg.config);
const forwardMessages = new Set(['incoming-updates', 'diff']);

module.exports = function(client) {
let sendSessionList = () => client.send('rv-session-list', tunnels.list().map(upgradeSession));

tunnels
.on('clusterDestroy', sendSessionList)
.on('clusterCreate', sendSessionList);

client
.on('rv-ping', function() {
debug('ping');
client.send('rv-pong');
})
.on('rv-get-session', function(data) {
var origin = data && data.localSite;
.on('rv-get-session', data => {
var origin = data && (data.origin || data.localSite);
debug('get session for %s', origin);
client.send('rv-session', sessionPayload(origin));
})
.on('rv-get-session-list', function() {
var sessions = tunnelController.list()
.map(function(session) {
// if this is a local server, rewrite its localSite to server docroot
var localServer = fileServerController.find(session.localSite);
if (localServer) {
session = extend(session, {localSite: localServer.rv.docroot});
}
return session;
});
client.send('rv-session-list', sessions);
})
.on('rv-create-session', function(data) {
.on('rv-get-session-list', sendSessionList)
.on('rv-create-session', data => {
data = upgradePayload(data);
debug('create session %o', data);

var onError = function(err) {
debug('error when creating session for %s: %s', data.localSite, err ? err.message : 'unknown');
tunnels.connect(data)
.then(cluster => {
debug('created connection for %s', data.origin);
client.send('rv-session', sessionPayload(data.origin));
})
.catch(err => {
debug('error when creating session for %s: %s', data.origin, err ? err.message : 'unknown');
var message = err ? err.message : 'Unable to establish tunnel with Remote View server';
client.send('rv-session', {
localSite: data.localSite,
origin: data.origin,
error: message + '. Please try again later.'
});
};

var onConnect = function() {
debug('created session for %s', data.localSite);
client.send('rv-session', sessionPayload(data.localSite));
this.removeListener('destroy', onDestroy);
};

var onDestroy = function(err) {
err && onError(err);
this.removeListener('connect', onConnect);
};

tunnelController.create(data)
.once('connect', onConnect)
.once('destroy', onDestroy);
});
})
.on('rv-close-session', function(data) {
.on('rv-close-session', data => {
data = upgradePayload(data);
debug('close session %o', data);
module.exports.closeRvSession(data.localSite);
closeRvSession(data.origin);
})
.on('rv-create-http-server', function(data) {
fileServerController(data.docroot)
.then(function(origin) {
debug('Explicit HTTP server creation is deprecated, use "rv-create-session" directly with file:// origin');
fileServer(data.docroot)
.then(server => {
client.send('rv-http-server', {
docroot: data.docroot,
origin
origin: server.host
});
})
.catch(function(err) {
.catch(err => {
client.send('rv-http-server', {
docroot: data.docroot,
error: err.message
});
});
});

fileServerController.forward(client);
setupMessageForwarding(client);
sendSessionList();

return client;
};

module.exports.closeRvSession = function(key) {
const closeRvSession = module.exports.closeRvSession = function(key) {
debug('requested session %o close', key);
var session = sessionPayload(key);
if (session && !session.error) {
debug('closing %s', session.publicId);
tunnelController.close(session.publicId);
tunnels.close(session.publicId);
}
};

module.exports.tunnels = tunnels;

function upgradePayload(data) {
if (data.localSite && !data.origin) { // v1.0
data = Object.assign({}, data, {origin: data.localSite});
}
return data;
}

function findSession(key) {
for (let session of tunnelController.list()) {
if (session.localSite === key || session.publicId === key) {
for (let session of tunnels.list()) {
if (session.origin === key || session.localSite === key || session.publicId === key) {
return session;
}
}
}

function sessionPayload(localSite) {
var session = findSession(localSite);
if (!session) {
// mayabe its a local web-server?
var localServer = fileServerController.find(localSite);
if (localServer) {
session = findSession(localServer.rv.address);
if (session) {
session = extend(session, {localSite})
}
}
function sessionPayload(origin) {
var session = findSession(origin);
if (session && session.state !== 'destroyed') {
return upgradeSession(session);
}

return session || {
localSite,
return {
origin,
localSite: origin,
error: 'Session not found'
};
}

function upgradeSession(session) {
return Object.assign({}, session, {state: 'connected'});
}

/**
* Setup LiveStyle message forwarding from pages with `file://` origin to their
* temporary file servers created for Remote View sessions
* @param {LiveStyleClient}
*/
function setupMessageForwarding(client) {
let onMessage = function(payload) {
if (typeof payload === 'string') {
payload = JSON.parse(payload);
}

if (!payload || !payload.data || !forwardMessages.has(payload.name)) {
return debug('skip message forward: unsupported message "%s"', payload.name);
}

// is this a filesystem?
if (!/^file:/.test(payload.data.uri)) {
return debug('skip message forward: "%s" is not a file origin', payload.data.uri);
}

let file = fileServer.normalizePath(payload.data.uri);
let servers = fileServer.list(file);
if (!servers.length) {
return debug('skip message forward: no matching servers for "%s" uri', payload.data.uri);
}

servers.forEach(server => {
// rebuild URL and forward message
let relative = file.slice(server.docroot.length).replace(/[\\\/]/g, '/');
let uri = server.host + '/' + relative;

debug('forward message to %s', uri);
client.send(payload.name, Object.assign({}, payload.data, {uri}));
});
};

client.on('message', onMessage);
return () => client.removeListener('message', onMessage);
}

if (require.main === module) {
let pkg = require('./package.json');
require('./lib/client')(pkg.config.websocketUrl, function(err, client) {
Expand All @@ -134,4 +173,4 @@ if (require.main === module) {
module.exports(client);
console.log('RV client connected');
});
}
}
17 changes: 8 additions & 9 deletions lib/client.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
/**
* A minimal LiveStyle server client. Unlike existing
* A minimal LiveStyle server client. Unlike existing
* `livestyle/client`, this one will not reconnect when connection
* in dropped. Instead, it will start its own WeSocket server instance.
*/
'use strict';

var WebSocket = require('ws');
var parseUrl = require('url').parse;
var debug = require('debug')('lsapp:client');
var extend = require('xtend');
var createServer = require('./server');
const WebSocket = require('ws');
const parseUrl = require('url').parse;
const debug = require('debug')('lsapp:client');
const createServer = require('./server');

var errCount = 0;
var defaultOptions = {
Expand All @@ -24,11 +23,11 @@ var connect = module.exports = function(url, options, callback) {
}

callback = callback || noop;
options = extend(defaultOptions, options || {});
options = Object.assign({}, defaultOptions, options || {});

debug('connecting to %s', url);
var client = new WebSocket(url);

return client
.on('message', onMessage)
.once('open', function() {
Expand Down Expand Up @@ -98,4 +97,4 @@ function wrapClient(client) {
}
};
return client;
}
}
18 changes: 8 additions & 10 deletions lib/controller/app-model.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,12 @@
*/
'use strict';

var debug = require('debug')('lsapp:app-model');
var tunnelController = require('./tunnel');
var appsDfn = require('../apps');
var googleChrome = require('../google-chrome');
var sublimeText = require('../sublime-text');
const debug = require('debug')('lsapp:app-model');
const appsDfn = require('../apps');
const googleChrome = require('../google-chrome');
const sublimeText = require('../sublime-text');

module.exports = function(model, client) {
tunnelController.on('update', sessions => model.set('rvSessions', sessions));
model.on('change', () => client.send('app-model', model.toJSON()));

var apps = {
Expand All @@ -24,12 +22,12 @@ module.exports = function(model, client) {

return {
install(id) {
return apps[id]
return apps[id]
? apps[id].install()
: Promise.reject(new Error(`Unknown app ${id}`));
},
detect(id) {
return apps[id]
return apps[id]
? apps[id].detect()
: Promise.reject(new Error(`Unknown app ${id}`));
}
Expand All @@ -40,7 +38,7 @@ function setupApp(app, handler, model, client) {
var attributeName = app.id;
var installPromise = null;
var autoupdater;

if (handler.autoupdate) {
autoupdater = handler.autoupdate(app)
.on('shouldUpdate', app => install('updating'))
Expand Down Expand Up @@ -120,4 +118,4 @@ if (require.main === module) {
}
module.exports(client).on('change', () => console.log(this.attributes));
});
}
}
Loading