diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/serve.js | 97 |
1 files changed, 97 insertions, 0 deletions
diff --git a/lib/serve.js b/lib/serve.js index 0c1ede8..abf15c3 100644 --- a/lib/serve.js +++ b/lib/serve.js @@ -21,6 +21,7 @@ var htime = require('human-time') var ph = require('pull-hyperscript') var emojis = require('emoji-named-characters') var jpeg = require('jpeg-autorotate') +var unzip = require('unzip') module.exports = Serve @@ -335,6 +336,7 @@ Serve.prototype.path = function (url) { case '/npm-prebuilds': return this.npmPrebuilds(m[2]) case '/npm-readme': return this.npmReadme(m[2]) case '/markdown': return this.markdown(m[2]) + case '/zip': return this.zip(m[2]) } return this.respond(404, 'Not found') } @@ -2202,6 +2204,101 @@ Serve.prototype.markdown = function (url) { ) } +Serve.prototype.zip = function (url) { + var self = this + var parts = url.split('/').slice(1) + var id = decodeURIComponent(parts.shift()) + var filename = parts.join('/') + var blobs = self.app.sbot.blobs + var etag = id + filename + var index = filename === '' || /\/$/.test(filename) + var indexFilename = index && (filename + 'index.html') + if (filename === '/' || /\/\/$/.test(filename)) { + // force directory listing if path ends in // + filename = filename.replace(/\/$/, '') + indexFilename = false + } + var files = index && [] + if (self.req.headers['if-none-match'] === etag) return self.respond(304) + blobs.size(id, function (err, size) { + if (size == null) return askWantBlobsForm([id]) + if (err) { + if (/^invalid/.test(err.message)) return self.respond(400, err.message) + else return self.respond(500, err.message || err) + } + var parseUnzip = unzip.Parse() + var gotEntry = false + parseUnzip.on('entry', function (entry) { + if (index) { + if (!gotEntry) { + if (entry.path === indexFilename) { + gotEntry = true + return serveFile(entry) + } else if (entry.path.substr(0, filename.length) === filename) { + files.push({path: entry.path, type: entry.type, props: entry.props}) + } + } + } else { + if (!gotEntry && entry.path === filename) { + gotEntry = true + // if (false && entry.type === 'Directory') return serveDirectory(entry) + return serveFile(entry) + } + } + entry.autodrain() + }) + parseUnzip.on('close', function () { + if (gotEntry) return + if (!index) return self.respond(404, 'Entry not found') + pull( + ph('section', {}, [ + ph('h3', [ + ph('a', {href: self.app.render.toUrl('/links/' + id)}, id.substr(0, 8) + '…'), + ' ', + ph('a', {href: self.app.render.toUrl('/zip/' + encodeURIComponent(id) + '/' + filename)}, filename || '/'), + ]), + pull( + pull.values(files), + pull.map(function (file) { + var path = '/zip/' + encodeURIComponent(id) + '/' + file.path + return ph('li', [ + ph('a', {href: self.app.render.toUrl(path)}, file.path) + ]) + }) + ) + ]), + self.wrapPage(id + filename), + self.respondSink(200) + ) + gotEntry = true // so that the handler on error event does not run + }) + parseUnzip.on('error', function (err) { + if (!gotEntry) return self.respond(400, err.message) + }) + var size + function serveFile(entry) { + size = entry.size + pull( + toPull.source(entry), + ident(gotType), + self.respondSink() + ) + } + pull( + self.app.getBlob(id), + toPull(parseUnzip) + ) + function gotType(type) { + type = type && mime.lookup(type) + if (type) self.res.setHeader('Content-Type', type) + if (size) self.res.setHeader('Content-Length', size) + self.res.setHeader('Cache-Control', 'public, max-age=315360000') + self.res.setHeader('etag', etag) + self.res.writeHead(200) + } + }) +} + // wrap a binary source and render it or turn into an embed Serve.prototype.wrapBinary = function (opts) { var self = this |