UNPKG

tube

Version:

Experimental Resource Loader implementing using Server Sent Events

112 lines (94 loc) 3.6 kB
var async = require('async'), debug = require('debug')('tube'), fs = require('fs'), path = require('path'), http = require('http'), fstream = require('fstream'), Manifest = require('../src/manifest'), reLineBreak = /\r?\n/g, defaultAssetPath = path.resolve(path.dirname(module.parent.filename), 'assets'); function loadManifest(req, opts, callback) { var manifestLocation = opts.manifest || defaultAssetPath, manifest = new Manifest(); console.log(req.url); // stat the manifest fs.stat(manifestLocation, function(err, stats) { if (err) return callback(err); // if we have a directory, then load the files from the directory if (stats.isDirectory()) { fstream.Reader({ path: manifestLocation }) .on('child', function(file) { if (file.type === 'File') { manifest.add({ path: file.path, size: file.size }); } }) .on('end', function() { callback(null, manifest); }); } else { return callback(); } }); } function loadManifestContent(manifest, callback) { async.map(manifest.files, fs.readFile, function(err, results) { if (err) return callback(err); // iterate through the results and update the manifest resources results.forEach(function(buffer, index) { manifest.resources[index].data = buffer.toString(); }); callback(null, manifest); }); } function reportError(err, res) { res.write('event: exception\n'); res.write('data: ' + err.message + '\n\n'); return res.end(); } function supplyAsset(res, separator) { // ensure we have a separator (default to 0x214B - ⅋) separator = separator || String.fromCharCode(0x214B); return function(resource, callback) { debug('attempting to load asset: ' + resource.path); fs.readFile(resource.path, 'utf8', function(err, data) { if (err) return callback(err); res.write('event: resource\n'); res.write('data: ' + JSON.stringify({ data: data.split(reLineBreak).join(separator) }) + '\n\n'); }); }; } module.exports = function(opts) { // ensure we have opts opts = opts || {}; return function(req, res) { loadManifest(req, opts, function(err, manifest) { if (err) { debug('received error: ' + err); } req.once('end', function() { console.log('request aborted'); res.end(); }); // if we hit an error, then provide a 204 which tells the EventSource to stop // attempting to reconnect // see: http://www.w3.org/TR/eventsource/#processing-model res.writeHead(err ? 204 : 200, { "content-type":"text/event-stream", "Cache-Control":"no-cache", "Connection":"keep-alive" }); if (err) return reportError(err, res); async.forEach(manifest.resources, supplyAsset(res, opts.separator), function(err) { return reportError(err, res); res.write('event: done\n\n'); res.end(); }); }); }; };