Created
February 23, 2018 14:12
-
-
Save kokobd/6a64b0f01e19ebadfb21a40d81ad4d88 to your computer and use it in GitHub Desktop.
Custom irunner.js that enables static file serving for GHCJSi
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
GHCJSi communication | |
reads messages from stdin, sends over stderr | |
*/ | |
var h$GHCJSiRecord = // true || | |
!!process.env['GHCJS_RECORD_GHCJSI']; | |
var h$GHCJSiPort = process.env['GHCJSI_PORT'] || 6400; | |
/* An optional static directory to serve at root */ | |
var h$GHCJSiStaticDir = process.env['GHCJSI_STATIC_DIR']; | |
var h$GHCJSiReplay = process.argv.length > 0 && | |
process.argv[process.argv.length-1] === 'replay'; | |
var h$GHCJSi = { data: null | |
, loadedSymbols: {} | |
, current: null | |
, sendMessage: h$sendMessage | |
, done: h$GHCJSiDone | |
, clientHtml: '' | |
, clientJS: '' | |
, socket: null | |
}; | |
global.h$GHCJSi = h$GHCJSi; | |
global.require = require; | |
global.module = module; | |
var fs = require('fs'); | |
var server = require('http').createServer(h$handleHTTP); | |
var url = require('url'); | |
var io = null; | |
try { | |
io = require('socket.io')(server); | |
} catch (e) { } | |
if (h$GHCJSiStaticDir) { | |
try { | |
/* Try to set up a static file server for GHCJSi */ | |
var finalhandler = require('finalhandler'); | |
var serveStatic = require('serve-static'); | |
var serveStaticDir = serveStatic(h$GHCJSiStaticDir); | |
} catch (e) { | |
console.log("\ncan't set up static file server for " + h$GHCJSiStaticDir); | |
console.log("\nmake sure you have finalhandler and serve-static installed:"); | |
console.log("\n\n npm install finalhandler serve-static\n"); | |
} | |
} | |
// start listening | |
function h$initGHCJSi() { | |
process.stdin.setEncoding('utf8'); | |
process.stderr.setEncoding('binary'); | |
process.on('uncaughtException', function(err) { | |
console.log(err); | |
console.log(err.stack); | |
}); | |
if(h$GHCJSiReplay) { | |
h$replayMessage(1); | |
} else { | |
h$startHTTPServer(); | |
process.stdin.on('readable', function() { | |
while(true) { | |
var str = process.stdin.read(); | |
if(str) { | |
var buf = new Buffer(str, 'hex'); | |
h$GHCJSi.data = h$GHCJSi.data ? Buffer.concat([h$GHCJSi.data, buf]) : buf; | |
h$processInput(); | |
} else { | |
return; | |
} | |
} | |
}); | |
process.stdin.on('close', function() { process.exit(0); }); | |
} | |
} | |
function h$replayMessage(n) { | |
try { | |
var buffer = fs.readFileSync("ghcjsimessage." + n + ".dat"); | |
var msgType = buffer.readUInt32BE(0); | |
h$processMessage(msgType, buffer.slice(4)); | |
setTimeout(function() { h$replayMessage(n+1); }, 1500); | |
} catch(e) { } | |
} | |
function h$startHTTPServer() { | |
if(!io) { | |
console.log("\nsocket.io not found, browser session not available"); | |
return; | |
} else { | |
console.log("\nsocket.io found, browser session available at http://localhost:" + h$GHCJSiPort); | |
if (typeof serveStaticDir !== 'undefined') { | |
console.log("\nserving static files from " + h$GHCJSiStaticDir); | |
} | |
} | |
io.on('connection', function(socket) { | |
console.log("\nbrowser connected, code runs in browser from now on"); | |
h$GHCJSi.socket = io; | |
socket.on('msg', function(msg) { | |
h$GHCJSi.sendMessage(msg.type, msg.payload); | |
}); | |
socket.on('out', function(data) { | |
process.stdout.write(data); | |
}); | |
socket.on('disconnect', function() { | |
// console.log('browser disconnected'); | |
}); | |
}); | |
h$GHCJSi.clientHtml = fs.readFileSync(h$GHCJSiStaticDir + '/ghcjsiClient.html'); | |
h$GHCJSi.clientJs = fs.readFileSync(__dirname + '/ghcjsiClient.js'); | |
server.listen(h$GHCJSiPort); | |
} | |
function h$handleHTTP(req, res) { | |
var u = url.parse(req.url); | |
if(u.pathname === '/' || u.pathname === '/index.html') { | |
res.writeHead(200, { 'Content-Type': 'text/html' }); | |
res.end(h$GHCJSi.clientHtml); | |
} else if(u.pathname === '/client.js') { | |
res.writeHead(200, { 'Content-Type': 'application/javascript' }); | |
res.end(h$GHCJSi.clientJs); | |
} else if (typeof serveStaticDir !== 'undefined') { | |
serveStaticDir(req, res, finalhandler(req, res)); | |
} else { | |
res.statusCode = 404; | |
res.statusMessage = 'not found'; | |
res.end(); | |
} | |
} | |
var h$GHCJSiMessageN = 0; | |
function h$processInput() { | |
while(h$GHCJSi.data && h$GHCJSi.data.length >= 8) { | |
var msgLength = h$GHCJSi.data.readUInt32BE(0); | |
var msgType = h$GHCJSi.data.readUInt32BE(4); | |
if(h$GHCJSi.data.length >= msgLength + 8) { | |
if(h$GHCJSiRecord && !h$GHCJSiReplay) { | |
fs.writeFileSync("ghcjsimessage." + (++h$GHCJSiMessageN) + ".dat" | |
,h$GHCJSi.data.slice(4, msgLength+8)); | |
} | |
var msgPayload = h$GHCJSi.data.slice(8, msgLength + 8); | |
h$GHCJSi.data = h$GHCJSi.data.slice(msgLength + 8); | |
if(h$GHCJSi.socket) { | |
h$GHCJSi.socket.emit('msg', { type: msgType, payload: msgPayload }); | |
} else { | |
h$processMessage(msgType, msgPayload); | |
} | |
} else return; | |
} | |
} | |
function h$processMessage(msgType, msgPayload) { | |
switch(msgType) { | |
case 0: // load initial code/rts and init | |
h$loadInitialCode(msgPayload.toString('utf8')); | |
h$sendMessage(0); | |
break; | |
case 1: // load code | |
h$loadCodeStr(msgPayload.toString('utf8')); | |
h$sendMessage(0); | |
break; | |
case 2: // run action | |
var symb = msgPayload.toString('utf8'); | |
h$GHCJSi.current = h$main(h$GHCJSi.loadedSymbols[msgPayload.toString('utf8')]); | |
break; | |
case 3: // abort | |
if(h$GHCJSi.current) | |
h$killThread( h$GHCJSi.current | |
, h$baseZCControlziExceptionziBasezinonTermination); | |
break; | |
default: | |
throw new Error("unknown message type: " + msgType); | |
} | |
} | |
function h$GHCJSiDone(thread) { | |
h$sendMessage(0); | |
h$GHCJSi.current = null; | |
} | |
function h$sendMessage(msgType, msg, c) { | |
var hdr = new Buffer(8); | |
hdr.writeUInt32BE(msg ? msg.length : 0, 0); | |
hdr.writeUInt32BE(msgType, 4); | |
process.stderr.write( msg ? Buffer.concat([hdr, msg]) : hdr, 'binary' | |
, function() { if(c) c(); }); | |
} | |
// load the RTS and set up the standard streams | |
function h$loadInitialCode(code) { | |
h$loadCodeStr(code, true); | |
// don't allow Haskell to read from stdin (fixme!) | |
h$base_stdin_fd.read = function(fd, fdo, buf, buf_offset, n, c) { c(0); } | |
// redirect Haskell's stderr to stdout since we use stderr to communicate (fixme!) | |
h$base_stderr_fd.write = h$base_stdout_fd.write; | |
} | |
function h$loadCodeStr(str) { | |
eval.call(null, str); | |
} | |
h$initGHCJSi(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment