Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Respond with a 206 and partial content to HTTP "Range" request header…

…s in the "staticProvider" middleware. Includes test cases to make sure it's working right.
  • Loading branch information...
commit 4f8ba72a9c3926e71613f1d407b2050bc50fdaab 1 parent e5762ba
@TooTallNate TooTallNate authored
Showing with 99 additions and 13 deletions.
  1. +38 −12 lib/connect/middleware/staticProvider.js
  2. +61 −1 test/static.test.js
View
50 lib/connect/middleware/staticProvider.js
@@ -73,23 +73,49 @@ module.exports = function staticProvider(options){
return next();
}
- // Serve the file directly using buffers
- function onRead(err, data) {
- if (err) return next(err);
-
- // Zero length buffers act funny, use a string
- if (data.length === 0) data = '';
-
- res.writeHead(200, {
+ var responseCode = 200,
+ responseHeaders = {
"Content-Type": utils.mime.type(filename),
- "Content-Length": data.length,
+ "Content-Length": stat.size,
"Last-Modified": stat.mtime.toUTCString(),
"Cache-Control": "public max-age=" + maxAge
- });
- res.end(data);
+ }, readOpts = {};
+
+ // If the client requested a "Range", then prepare the headers
+ // and set the read stream options to read the specified range.
+ if (req.headers['range']) {
+ var range = req.headers['range'].substring(6).split('-');
+ readOpts.start = Number(range[0]);
+ readOpts.end = Number(range[1]);
+ if (range[1].length === 0) {
+ readOpts.end = stat.size - 1;
+ } else if (range[0].length === 0) {
+ readOpts.end = stat.size - 1;
+ readOpts.start = readOpts.end - range[1] + 1;
+ }
+ responseCode = 206;
+ responseHeaders['Accept-Ranges'] = "bytes";
+ responseHeaders['Content-Length'] = readOpts.end - readOpts.start + 1;
+ responseHeaders['Content-Range'] = "bytes " + readOpts.start + "-" + readOpts.end + "/" + stat.size;
}
- fs.readFile(filename, onRead);
+ // Stream the file
+ res.writeHead(responseCode, responseHeaders);
+ var readStream = fs.createReadStream(filename, readOpts);
+ readStream.addListener('error', function(error) {
+ next(error);
+ });
+ readStream.on('data', function(data) {
+ if (!res.write(data)) {
+ readStream.pause();
+ }
+ });
+ res.on('drain', function() {
+ readStream.resume();
+ });
+ readStream.on('end', function() {
+ res.end();
+ });
});
};
View
62 test/static.test.js
@@ -77,11 +77,71 @@ module.exports = {
);
server.assertResponse('GET', '/foo.json', 404, 'Cannot GET /foo.json', 'Test invalid static file.');
},
-
+
'test directory': function(){
var server = helpers.run(
connect.staticProvider({ root: __dirname })
);
server.assertResponse('GET', '/fixtures', 404, 'Cannot GET /fixtures');
+ },
+
+ 'test range request header beginning': function() {
+ var server = helpers.run(
+ connect.staticProvider(fixturesPath)
+ );
+ var req = server.request('GET', '/index.html', { 'Range': 'bytes=9-'});
+ req.buffer = true;
+ req.addListener('response', function(res){
+ res.addListener('end', function(){
+ var headers = res.headers,
+ body = "</p>";
+ assert.equal(206, res.statusCode, 'Test static with range request header beginning status code');
+ assert.equal(body, res.body, 'Test static with range request header beginning response body');
+ assert.equal(body.length, headers['content-length'], 'Test static with range request header beginning Content-Length');
+ assert.equal("bytes", headers['accept-ranges'], 'Test static with range request header beginning Accept-Ranges');
+ assert.equal("bytes 9-12/13", headers['content-range'], 'Test static with range request header beginning Content-Range');
+ });
+ });
+ req.end();
+ },
+
+ 'test range request header middle': function() {
+ var server = helpers.run(
+ connect.staticProvider(fixturesPath)
+ );
+ var req = server.request('GET', '/index.html', { 'Range': 'bytes=9-12'});
+ req.buffer = true;
+ req.addListener('response', function(res){
+ res.addListener('end', function(){
+ var headers = res.headers,
+ body = "</p>";
+ assert.equal(206, res.statusCode, 'Test static with range request header middle status code');
+ assert.equal(body, res.body, 'Test static with range request header middle response body');
+ assert.equal(body.length, headers['content-length'], 'Test static with range request header middle Content-Length');
+ assert.equal("bytes", headers['accept-ranges'], 'Test static with range request header middle Accept-Ranges');
+ assert.equal("bytes 9-12/13", headers['content-range'], 'Test static with range request header end Content-Range');
+ });
+ });
+ req.end();
+ },
+
+ 'test range request header end': function() {
+ var server = helpers.run(
+ connect.staticProvider(fixturesPath)
+ );
+ var req = server.request('GET', '/index.html', { 'Range': 'bytes=-4'});
+ req.buffer = true;
+ req.addListener('response', function(res){
+ res.addListener('end', function(){
+ var headers = res.headers,
+ body = "</p>";
+ assert.equal(206, res.statusCode, 'Test static with range request header end status code');
+ assert.equal(body, res.body, 'Test static with range request header end response body');
+ assert.equal(body.length, headers['content-length'], 'Test static with range request header end Content-Length');
+ assert.equal("bytes", headers['accept-ranges'], 'Test static with range request header end Accept-Ranges');
+ assert.equal("bytes 9-12/13", headers['content-range'], 'Test static with range request header end Content-Range');
+ });
+ });
+ req.end();
}
}
Please sign in to comment.
Something went wrong with that request. Please try again.