| // requires | |
| var http = require('http'); | |
| var path = require('path'); | |
| var url = require('url'); | |
| var fs = require('fs'); | |
| var child_process = require('child_process'); | |
| // constants | |
| var PORT = +process.env.PORT || 9292; | |
| var HOST = process.env.HOST || '0.0.0.0'; | |
| // main | |
| http.createServer(serveRequest).listen(PORT, HOST); | |
| console.log('listening on '+HOST+':'+PORT); | |
| run_make_test(); | |
| 'src test Makefile package.json'.split(' ').forEach(function(filename) { | |
| recursivelyWatch(filename, run_make_test); | |
| }); | |
| // functions | |
| function serveRequest(req, res) { | |
| var reqTime = new Date; | |
| enqueueOrDo(function() { | |
| var filepath = path.normalize(url.parse(req.url).pathname).slice(1); | |
| fs.readFile(filepath, function(err, data) { | |
| if (err) { | |
| if (err.code === 'ENOENT' || err.code === 'EISDIR') { | |
| res.statusCode = 404; | |
| res.end('404 Not Found: /' + filepath + '\n'); | |
| } | |
| else { | |
| console.log(err); | |
| res.statusCode = 500; | |
| res.end('500 Internal Server Error: ' + err.code + '\n'); | |
| } | |
| } | |
| else { | |
| var ext = filepath.match(/\.[^.]+$/); | |
| if (ext) res.setHeader('Content-Type', 'text/' + ext[0].slice(1)); | |
| res.end(data); | |
| } | |
| console.log('[%s] %s %s /%s - %s%sms', | |
| reqTime.toISOString(), res.statusCode, req.method, filepath, | |
| (data ? (data.length >> 10) + 'kb, ' : ''), Date.now() - reqTime); | |
| }); | |
| }); | |
| } | |
| function recursivelyWatch(watchee, cb) { | |
| fs.readdir(watchee, function(err, files) { | |
| if (err) { // not a directory, just watch it | |
| fs.watch(watchee, cb); | |
| } | |
| else { // a directory, recurse, also watch for files being added or deleted | |
| files.forEach(recurse); | |
| fs.watch(watchee, function() { | |
| fs.readdir(watchee, function(err, filesNew) { | |
| if (err) return; // watchee may have been deleted | |
| // filesNew - files = new files or dirs to watch | |
| filesNew.filter(function(file) { return files.indexOf(file) < 0; }) | |
| .forEach(recurse); | |
| files = filesNew; | |
| }); | |
| cb(); | |
| }); | |
| } | |
| function recurse(file) { recursivelyWatch(path.join(watchee, file), cb); } | |
| }); | |
| } | |
| var q; | |
| function enqueueOrDo(cb) { q ? q.push(cb) : cb(); } | |
| function run_make_test() { | |
| if (q) return; | |
| q = []; | |
| console.log('[%s]\nmake test', (new Date).toISOString()); | |
| var make_test = child_process.exec('make test', { env: process.env }); | |
| make_test.stdout.pipe(process.stdout, { end: false }); | |
| make_test.stderr.pipe(process.stderr, { end: false }); | |
| make_test.on('exit', function(code) { | |
| if (code) { | |
| console.error('Exit Code ' + code); | |
| } else { | |
| console.log('\nMathQuill is now running on localhost:9292'); | |
| console.log('Open http://localhost:9292/test/demo.html\n'); | |
| } | |
| for (var i = 0; i < q.length; i += 1) q[i](); | |
| q = undefined; | |
| }); | |
| } |