aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorcel <cel@f/6sQ6d2CMxRUhLpspgGIulDxDCwYD7DzFzPNr7u5AU=.ed25519>2020-01-15 10:35:04 -0500
committercel <cel@f/6sQ6d2CMxRUhLpspgGIulDxDCwYD7DzFzPNr7u5AU=.ed25519>2020-01-15 10:39:09 -0500
commit306f74635bc6ba71bf7788c55dceffad7cc6cb27 (patch)
tree02f1a5f7d66052b347e136c6daf810b2fc642fbc
parentb8cd4fd2dcbbaf47e2a4167f169abeaa59d46173 (diff)
downloadpatchfoo-306f74635bc6ba71bf7788c55dceffad7cc6cb27.tar.gz
patchfoo-306f74635bc6ba71bf7788c55dceffad7cc6cb27.zip
Support blob range requests
-rw-r--r--lib/serve.js86
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)
+ }
}
})
}