msgflo
Version:
Polyglot FBP runtime based on message queues
145 lines (122 loc) • 4.38 kB
text/coffeescript
protocol = require './protocol'
coordinator = require './coordinator'
common = require './common'
querystring = require 'querystring'
transport = require('msgflo-nodejs').transport
debug = require('debug')('msgflo:runtime')
http = require 'http'
EventEmitter = require('events').EventEmitter
WebSocketServer = require('websocket').server
send = (connection, msg) ->
connection.sendUTF JSON.stringify msg
class WebSocketTransport extends EventEmitter
constructor: () ->
= []
ws = new WebSocketServer { httpServer: }
handleMessage = (message, connection) =>
return if message.type != 'utf8'
try
msg = JSON.parse(message.utf8Data)
catch e
return null
msg, connection
ws.on 'request', (request) =>
subProtocol = if request.requestedProtocols.indexOf("noflo") != -1 then "noflo" else null
connection = request.accept subProtocol, request.origin
.push connection
connection.on 'message', (message) ->
handleMessage message, connection
connection.on 'close', () =>
connIndex = .indexOf connection
return if connIndex == -1
.splice connIndex, 1
send: (protocol, command, payload, ctx) ->
connection = ctx
msg =
protocol: protocol
command: command
payload: payload
debug 'SEND', msg
send connection, msg
sendAll: (protocol, command, payload) ->
msg =
protocol: protocol
command: command
payload: payload
debug 'SENDALL', .length, msg
for connection in
send connection, msg
emitMessage: (msg, ctx) ->
'message', msg.protocol, msg.command, msg.payload, ctx
# atomic
saveGraphFile = (graph, filepath, callback) ->
fs = require 'fs'
temppath = filepath + ".msgflo-autosave-#{Date.now()}"
json = JSON.stringify graph, null, 2
fs.open temppath, 'wx', (err, fd) ->
return callback err if err
fs.write fd, json, (err) ->
return callback err if err
fs.fsync fd, (err) ->
return callback err if err
fs.rename temppath, filepath, (err) ->
fs.unlink temppath, (e) ->
return callback err
class Runtime
constructor: () ->
= null
= null
= null
= transport.getBroker .broker
= new coordinator.Coordinator ,
= common.debounce () =>
debug 'saving graph changes', .graph
graph = .serializeGraph 'main'
saveGraphFile graph, .graph, (err) ->
console.log "ERROR: Failed to save graph file", err if err
, 500
if .graph and .autoSave
debug 'enabling autosave'
.on 'graph-changed', () =>
start: (callback) ->
= http.createServer()
= new WebSocketTransport
= protocol.Protocol ,
.on 'request', (request, response) =>
request, response
.listen .port, (err) =>
return callback err if err
.start (err) =>
return callback err if err
onLoaded = (err) =>
return callback err, ,
if .graph
.loadGraphFile .graph, , onLoaded
else
onLoaded null
stop: (callback) ->
.close callback
address: () ->
scheme = 'ws://'
address = scheme + .host + ':' + .port
liveUrl: () ->
params = querystring.escape "protocol=websocket&address=#{@address()}"
url = "#{@options.ide}#runtime/endpoint?#{params}"
serveFrontpage: (req, res) ->
html = """
<a id="flowhub_url">Open in Flowhub</a>
<script>
var addr = window.location.origin.replace("http://", "ws://");
addr = addr.replace("https://", "ws://");
var ide = "#{@options.ide}";
var url = ide+"/#runtime/endpoint?protocol=websocket&address="+encodeURIComponent(addr);
var a = document.getElementById("flowhub_url");
a.href = url;
</script>
"""
res.statusCode = 200
res.setHeader "Content-Type", "text/html"
res.write html
res.end()
exports.Runtime = Runtime