diff options
author | cel <cel@f/6sQ6d2CMxRUhLpspgGIulDxDCwYD7DzFzPNr7u5AU=.ed25519> | 2020-01-15 10:35:04 -0500 |
---|---|---|
committer | cel <cel@f/6sQ6d2CMxRUhLpspgGIulDxDCwYD7DzFzPNr7u5AU=.ed25519> | 2020-01-15 10:39:09 -0500 |
commit | 306f74635bc6ba71bf7788c55dceffad7cc6cb27 (patch) | |
tree | 02f1a5f7d66052b347e136c6daf810b2fc642fbc | |
parent | b8cd4fd2dcbbaf47e2a4167f169abeaa59d46173 (diff) | |
download | patchfoo-306f74635bc6ba71bf7788c55dceffad7cc6cb27.tar.gz patchfoo-306f74635bc6ba71bf7788c55dceffad7cc6cb27.zip |
Support blob range requests
-rw-r--r-- | lib/serve.js | 86 |
1 files changed, 68 insertions, 18 deletions
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) + } } }) } |