You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
105 lines
2.3 KiB
105 lines
2.3 KiB
|
|
var util = require('util');
|
|
var slice = require('stream-slice').slice;
|
|
var Stream = require('stream');
|
|
|
|
module.exports = async function (ctx, next) {
|
|
var range = ctx.header.range;
|
|
ctx.set('Accept-Ranges', 'bytes');
|
|
|
|
if (!range) {
|
|
return next();
|
|
}
|
|
|
|
var ranges = rangeParse(range);
|
|
|
|
if (!ranges || ranges.length == 0) {
|
|
ctx.status = 416;
|
|
return;
|
|
}
|
|
|
|
if (ctx.method == 'PUT') {
|
|
ctx.status = 400;
|
|
return;
|
|
}
|
|
|
|
await next();
|
|
|
|
if (ctx.method != 'GET' ||
|
|
ctx.body == null) {
|
|
return;
|
|
}
|
|
|
|
var first = ranges[0];
|
|
var rawBody = ctx.body;
|
|
var len = rawBody.length;
|
|
|
|
// avoid multi ranges
|
|
var firstRange = ranges[0];
|
|
var start = firstRange[0];
|
|
var end = firstRange[1];
|
|
|
|
if (!Buffer.isBuffer(rawBody)) {
|
|
if (rawBody instanceof Stream.Readable) {
|
|
len = ctx.length || '*';
|
|
rawBody = rawBody.pipe(slice(start, end + 1));
|
|
} else if (typeof rawBody !== 'string') {
|
|
rawBody = new Buffer(JSON.stringify(rawBody));
|
|
len = rawBody.length;
|
|
} else {
|
|
rawBody = new Buffer(rawBody);
|
|
len = rawBody.length;
|
|
}
|
|
}
|
|
|
|
//Adjust infinite end
|
|
if (end === Infinity) {
|
|
if (Number.isInteger(len)) {
|
|
end = len - 1;
|
|
} else {
|
|
// FIXME(Calle Svensson): If we don't know how much we can return, we do a normal HTTP 200 repsonse
|
|
ctx.status = 200;
|
|
return;
|
|
}
|
|
}
|
|
|
|
var args = [start, end+1].filter(function(item) {
|
|
return typeof item == 'number';
|
|
});
|
|
|
|
ctx.set('Content-Range', rangeContentGenerator(start, end, len));
|
|
ctx.status = 206;
|
|
|
|
if (rawBody instanceof Stream) {
|
|
ctx.body = rawBody;
|
|
} else {
|
|
ctx.body = rawBody.slice.apply(rawBody, args);
|
|
}
|
|
|
|
if (len !== '*') {
|
|
ctx.length = end - start + 1;
|
|
}
|
|
};
|
|
|
|
function rangeParse(str) {
|
|
var token = str.split('=');
|
|
if (!token || token.length != 2 || token[0] != 'bytes') {
|
|
return null;
|
|
}
|
|
return token[1].split(',')
|
|
.map(function(range) {
|
|
return range.split('-').map(function(value) {
|
|
if (value === '') {
|
|
return Infinity;
|
|
}
|
|
return Number(value);
|
|
});
|
|
})
|
|
.filter(function(range) {
|
|
return !isNaN(range[0]) && !isNaN(range[1]) && range[0] <= range[1];
|
|
});
|
|
}
|
|
|
|
function rangeContentGenerator(start, end, length) {
|
|
return util.format('bytes %d-%d/%s', start, end, length);
|
|
}
|
|
|