node-static
Advanced tools
Comparing version 0.7.4 to 0.7.5
@@ -18,2 +18,7 @@ #!/usr/bin/env node | ||
}) | ||
.option('host-address', { | ||
alias: 'a', | ||
'default': '127.0.0.1', | ||
description: 'the local network interface at which to listen' | ||
}) | ||
.option('cache', { | ||
@@ -105,5 +110,4 @@ alias: 'c', | ||
}).resume(); | ||
}).listen(+argv.port); | ||
}).listen(+argv.port, argv['host-address']); | ||
console.log('serving "' + dir + '" at http://127.0.0.1:' + argv.port); | ||
console.log('serving "' + dir + '" at http://' + argv['host-address'] + ':' + argv.port); |
@@ -11,3 +11,3 @@ var fs = require('fs') | ||
// Current version | ||
var version = [0, 7, 3]; | ||
var version = [0, 7, 5]; | ||
@@ -231,2 +231,40 @@ Server = function (root, options) { | ||
Server.prototype.parseByteRange = function(req, stat) { | ||
var byteRange = { | ||
from: 0, | ||
to: 0, | ||
valid: false | ||
} | ||
var rangeHeader = req.headers['range']; | ||
var flavor = 'bytes='; | ||
if (rangeHeader) { | ||
if (rangeHeader.indexOf(flavor) == 0 && rangeHeader.indexOf(',') == -1) { | ||
/* Parse */ | ||
rangeHeader = rangeHeader.substr(flavor.length).split('-'); | ||
byteRange.from = parseInt(rangeHeader[0]); | ||
byteRange.to = parseInt(rangeHeader[1]); | ||
/* Replace empty fields of differential requests by absolute values */ | ||
if (isNaN(byteRange.from) && !isNaN(byteRange.to)) { | ||
byteRange.from = stat.size - byteRange.to; | ||
byteRange.to = stat.size - 1; | ||
} else if (!isNaN(byteRange.from) && isNaN(byteRange.to)) { | ||
byteRange.to = stat.size - 1; | ||
} | ||
/* General byte range validation */ | ||
if (!isNaN(byteRange.from) && !!byteRange.to && 0 <= byteRange.from < byteRange.to) { | ||
byteRange.valid = true; | ||
} else { | ||
console.warn("Request contains invalid range header: ", rangeHeader); | ||
} | ||
} else { | ||
console.warn("Request contains unsupported range header: ", rangeHeader); | ||
} | ||
} | ||
return byteRange; | ||
} | ||
Server.prototype.respondNoGzip = function (pathname, status, contentType, _headers, files, stat, req, res, finish) { | ||
@@ -237,5 +275,25 @@ var mtime = Date.parse(stat.mtime), | ||
clientETag = req.headers['if-none-match'], | ||
clientMTime = Date.parse(req.headers['if-modified-since']); | ||
clientMTime = Date.parse(req.headers['if-modified-since']), | ||
startByte = 0, | ||
length = stat.size, | ||
byteRange = this.parseByteRange(req, stat); | ||
/* Handle byte ranges */ | ||
if (files.length == 1 && byteRange.valid) { | ||
if (byteRange.to < length) { | ||
// Note: HTTP Range param is inclusive | ||
startByte = byteRange.from; | ||
length = byteRange.to - byteRange.from + 1; | ||
status = 206; | ||
} else { | ||
byteRange.valid = false; | ||
console.warn("Range request exceeds file boundaries, goes until byte no", byteRange.to, "against file size of", length, "bytes"); | ||
} | ||
} | ||
/* In any case, check for unhandled byte range headers */ | ||
if (!byteRange.valid && req.headers['range']) { | ||
console.error(new Error("Range request present but invalid, might serve whole file instead")); | ||
} | ||
// Copy default headers | ||
@@ -250,3 +308,3 @@ for (var k in this.options.headers) { headers[k] = this.options.headers[k] } | ||
headers['Content-Type'] = contentType; | ||
headers['Content-Length'] = stat.size; | ||
headers['Content-Length'] = length; | ||
@@ -275,5 +333,6 @@ for (var k in _headers) { headers[k] = _headers[k] } | ||
} else { | ||
res.writeHead(status, headers); | ||
this.stream(key, files, new(buffer.Buffer)(stat.size), res, function (e, buffer) { | ||
this.stream(key, files, new(buffer.Buffer)(length), startByte, res, function (e, buffer) { | ||
if (e) { return finish(500, {}) } | ||
@@ -289,2 +348,3 @@ finish(status, headers); | ||
'application/octet-stream'; | ||
if(this.options.gzip) { | ||
@@ -297,3 +357,4 @@ this.respondGzip(pathname, status, contentType, _headers, files, stat, req, res, finish); | ||
Server.prototype.stream = function (pathname, files, buffer, res, callback) { | ||
Server.prototype.stream = function (pathname, files, buffer, startByte, res, callback) { | ||
(function streamFile(files, offset) { | ||
@@ -308,3 +369,5 @@ var file = files.shift(); | ||
flags: 'r', | ||
mode: 0666 | ||
mode: 0666, | ||
start: startByte, | ||
end: startByte + buffer.length - 1 | ||
}).on('data', function (chunk) { | ||
@@ -311,0 +374,0 @@ // Bounds check the incoming chunk and offset, as copying |
@@ -34,5 +34,5 @@ { | ||
}, | ||
"version" : "0.7.4", | ||
"version" : "0.7.5", | ||
"engines" : { "node": ">= 0.4.1" } | ||
} | ||
@@ -220,4 +220,8 @@ node-static | ||
# expose the server to your local network | ||
$ static -a 0.0.0.0 | ||
serving "." at http://0.0.0.0:8080 | ||
# show help message, including all options | ||
$ static -h | ||
``` |
@@ -114,2 +114,50 @@ var vows = require('vows') | ||
}).addBatch({ | ||
'serving first 5 bytes of hello.txt': { | ||
topic : function(){ | ||
var options = { | ||
url: TEST_SERVER + '/hello.txt', | ||
headers: { | ||
'Range': 'bytes=0-4' | ||
} | ||
}; | ||
request.get(options, this.callback); | ||
}, | ||
'should respond with 206' : function(error, response, body){ | ||
assert.equal(response.statusCode, 206); | ||
}, | ||
'should respond with text/plain': function(error, response, body){ | ||
assert.equal(response.headers['content-type'], 'text/plain'); | ||
}, | ||
'should have content-length of 5 bytes': function(error, response, body){ | ||
assert.equal(response.headers['content-length'], 5); | ||
}, | ||
'should respond with hello': function(error, response, body){ | ||
assert.equal(body, 'hello'); | ||
} | ||
} | ||
}).addBatch({ | ||
'serving last 5 bytes of hello.txt': { | ||
topic : function(){ | ||
var options = { | ||
url: TEST_SERVER + '/hello.txt', | ||
headers: { | ||
'Range': 'bytes=6-10' | ||
} | ||
}; | ||
request.get(options, this.callback); | ||
}, | ||
'should respond with 206' : function(error, response, body){ | ||
assert.equal(response.statusCode, 206); | ||
}, | ||
'should respond with text/plain': function(error, response, body){ | ||
assert.equal(response.headers['content-type'], 'text/plain'); | ||
}, | ||
'should have content-length of 5 bytes': function(error, response, body){ | ||
assert.equal(response.headers['content-length'], 5); | ||
}, | ||
'should respond with world': function(error, response, body){ | ||
assert.equal(body, 'world'); | ||
} | ||
} | ||
}).addBatch({ | ||
'serving directory index': { | ||
@@ -116,0 +164,0 @@ topic : function(){ |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
38226
789
227