From 306f74635bc6ba71bf7788c55dceffad7cc6cb27 Mon Sep 17 00:00:00 2001 From: cel Date: Wed, 15 Jan 2020 10:35:04 -0500 Subject: Support blob range requests --- lib/serve.js | 86 +++++++++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 68 insertions(+), 18 deletions(-) (limited to 'lib') diff --git a/lib/serve.js b/lib/serve.js index b4efc58..afe494e 100644 --- a/lib/serve.js +++ b/lib/serve.js @@ -1506,24 +1506,74 @@ Serve.prototype.blob = function (id, path) { if (/^invalid/.test(err.message)) return self.respond(400, err.message) else return self.respond(500, err.message || err) } - pull( - self.app.getBlob(id, key), - pull.map(Buffer), - ident(gotType), - self.respondSink() - ) - function gotType(type) { - type = type && mime.lookup(type) - if (type) self.res.setHeader('Content-Type', type) - // don't serve size for encrypted blob, because it refers to the size of - // the ciphertext - if (typeof size === 'number' && !key) - self.res.setHeader('Content-Length', size) - if (self.query.name) self.res.setHeader('Content-Disposition', - 'inline; filename='+encodeDispositionFilename(self.query.name)) - self.res.setHeader('Cache-Control', 'public, max-age=315360000') - self.res.setHeader('ETag', etag) - self.res.writeHead(200) + self.res.setHeader('Accept-Ranges', 'bytes') + var range = self.req.headers.range + if (range) { + if (self.req.headers['if-none-match'] === etag) return self.respond(304) + // TODO: support multiple ranges + var m = /^bytes=([0-9]*)-([0-9]*)$/.exec(range) + if (!m) return self.respond(416, 'Unable to parse range') + var start = m[1] + var last = m[2] + if (start === '') { + start = size - last + last = size - 1 + } else if (last === '') { + start = Number(start) + last = size - 1 + } else { + start = Number(start) + last = Number(last) + } + if (start > size || last >= size) return res.writeHead(416, 'Range not satisfiable') + var end = last + 1 + var length = end - start + var wroteHeaders = false + pull( + // TODO: figure out how to use readBlobSlice for private blob range request + key ? pull( + self.app.getBlob(id, key), + u.pullSlice(start, end) + ) : self.app.readBlobSlice({ + link: id, + size: size + }, { + start: start, + end: end, + }), + pull.through(function (buf) { + if (wroteHeaders) return + self.res.setHeader('Cache-Control', 'public, max-age=315360000') + self.res.setHeader('ETag', etag) + self.res.setHeader('Content-Length', length) + self.res.setHeader('Content-Range', 'bytes ' + start + '-' + last + '/' + size) + self.res.writeHead(206) + wroteHeaders = true + }), + pull.map(Buffer), + self.respondSink() + ) + + } else { + pull( + self.app.getBlob(id, key), + pull.map(Buffer), + ident(gotType), + self.respondSink() + ) + function gotType(type) { + type = type && mime.lookup(type) + if (type) self.res.setHeader('Content-Type', type) + // don't serve size for encrypted blob, because it refers to the size of + // the ciphertext + if (typeof size === 'number' && !key) + self.res.setHeader('Content-Length', size) + if (self.query.name) self.res.setHeader('Content-Disposition', + 'inline; filename='+encodeDispositionFilename(self.query.name)) + self.res.setHeader('Cache-Control', 'public, max-age=315360000') + self.res.setHeader('etag', etag) + self.res.writeHead(200) + } } }) } -- cgit v1.2.3