UNPKG

msgflo

Version:

Polyglot FBP runtime based on message queues

145 lines (122 loc) 4.38 kB
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: (@server) -> @connections = [] ws = new WebSocketServer { httpServer: @server } handleMessage = (message, connection) => return if message.type != 'utf8' try msg = JSON.parse(message.utf8Data) catch e return null @emitMessage msg, connection ws.on 'request', (request) => subProtocol = if request.requestedProtocols.indexOf("noflo") != -1 then "noflo" else null connection = request.accept subProtocol, request.origin @connections.push connection connection.on 'message', (message) -> handleMessage message, connection connection.on 'close', () => connIndex = @connections.indexOf connection return if connIndex == -1 @connections.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', @connections.length, msg for connection in @connections send connection, msg emitMessage: (msg, ctx) -> @emit '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: (@options) -> @server = null @transport = null @protocol = null @broker = transport.getBroker @options.broker @coordinator = new coordinator.Coordinator @broker, @options @saveGraph = common.debounce () => debug 'saving graph changes', @options.graph graph = @coordinator.serializeGraph 'main' saveGraphFile graph, @options.graph, (err) -> console.log "ERROR: Failed to save graph file", err if err , 500 if @options.graph and @options.autoSave debug 'enabling autosave' @coordinator.on 'graph-changed', () => @saveGraph() start: (callback) -> @server = http.createServer() @transport = new WebSocketTransport @server @protocol = protocol.Protocol @transport, @coordinator @server.on 'request', (request, response) => @serveFrontpage request, response @server.listen @options.port, (err) => return callback err if err @coordinator.start (err) => return callback err if err onLoaded = (err) => return callback err, @address(), @liveUrl() if @options.graph @coordinator.loadGraphFile @options.graph, @options, onLoaded else onLoaded null stop: (callback) -> @server.close callback address: () -> scheme = 'ws://' address = scheme + @options.host + ':' + @options.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