Comparing version 1.10.6 to 1.10.8
<!-- vim:ts=4:sts=4:sw=4:et:tw=60 --> | ||
## 1.10.7-8 | ||
- Fixes support for range content requests, such that Q-IO based web serves can | ||
host static audio and video content to the web. Further work needed for the | ||
more escoteric non-contiguous multi-range requests. | ||
- Allow `copyTree` to write over existing trees. (@nerfin) | ||
## 1.10.6 | ||
- Restores support for host negotiation terms. | ||
- Restores the "request.terms.host" property to report which host pattern was | ||
selected by a host negotiator. | ||
@@ -7,0 +15,0 @@ ## 1.10.5 |
@@ -162,4 +162,4 @@ var Q = require("q"); | ||
} else if (stat.isDirectory()) { | ||
return Q.when(self.makeDirectory(target), function () { | ||
return Q.when(self.list(source), function (list) { | ||
return self.exists(target).then(function (targetExists) { | ||
var copySubTree = Q.when(self.list(source), function (list) { | ||
return Q.all(list.map(function (child) { | ||
@@ -172,2 +172,9 @@ return self.copyTree( | ||
}); | ||
if (targetExists) { | ||
return copySubTree; | ||
} else { | ||
return Q.when(self.makeDirectory(target), function () { | ||
return copySubTree; | ||
}); | ||
} | ||
}); | ||
@@ -174,0 +181,0 @@ } else if (stat.isSymbolicLink()) { |
@@ -109,7 +109,11 @@ | ||
// Truncate to the first requested continuous range | ||
range = interpretFirstRange(request.headers["range"]); | ||
range = interpretFirstRange(request.headers["range"], stat.size); | ||
// Like Apache, ignore the range header if it is invalid | ||
if (range) { | ||
if (range.end > stat.size) | ||
if (range.end > stat.size) { | ||
range.end = stat.size; | ||
} | ||
if (range.end <= range.begin) { | ||
return StatusApps.responseForStatus(request, 416); // not satisfiable | ||
} | ||
status = 206; // partial content | ||
@@ -122,5 +126,7 @@ headers["content-range"] = ( | ||
headers["content-length"] = "" + (range.end - range.begin); | ||
options.begin = range.begin; | ||
options.end = range.end; | ||
} else { | ||
return StatusApps.responseForStatus(request, 416); // not satisfiable | ||
} | ||
options.begin = range.begin; | ||
options.end = range.end; | ||
} | ||
@@ -159,4 +165,4 @@ // Full requests | ||
if (match[1] == "") { | ||
begin = size - match[2]; | ||
end = size; | ||
begin = 0; | ||
end = +match[2] + 1; | ||
} else if (match[2] == "") { | ||
@@ -183,8 +189,6 @@ begin = +match[1]; | ||
var next = interpretRange(texts[i], size); | ||
if (!next) | ||
break; | ||
if (next.begin <= range.end) { | ||
range.end = next.end; | ||
} else { | ||
break; | ||
return; // Can't satisfy non-contiguous ranges TODO | ||
} | ||
@@ -191,0 +195,0 @@ } |
@@ -103,5 +103,3 @@ | ||
) { | ||
if (!request.terms) { | ||
request.terms = {}; | ||
} | ||
request.terms = request.terms || {}; | ||
request.terms.host = pattern; | ||
@@ -108,0 +106,0 @@ return app(request); |
{ | ||
"name": "q-io", | ||
"version": "1.10.6", | ||
"version": "1.10.8", | ||
"description": "IO using Q promises", | ||
@@ -5,0 +5,0 @@ "homepage": "http://github.com/kriskowal/q-io/", |
@@ -33,3 +33,3 @@ | ||
.then(function (content) { | ||
expect(content).toEqual("1234.txt\n5678.txt\n9012/\n"); | ||
expect(content).toEqual("01234.txt\n1234.txt\n5678.txt\n9012/\n"); | ||
}) | ||
@@ -74,2 +74,3 @@ .finally(server.stop); | ||
" <ul class=\"directory-index\">\n" + | ||
" <li class=\"entry file\"><a href=\"01234.txt\">01234.txt</a></li>\n" + | ||
" <li class=\"entry file\"><a href=\"1234.txt\">1234.txt</a></li>\n" + | ||
@@ -76,0 +77,0 @@ " <li class=\"entry file\"><a href=\"5678.txt\">5678.txt</a></li>\n" + |
@@ -19,8 +19,8 @@ | ||
{ | ||
description: "The final 500 bytes (byte offsets 9500-9999, inclusive)", | ||
input: "bytes=-500", | ||
oracle: {begin: 9500, end: 10000} | ||
description: "The initial 500 bytes with 0 elided", | ||
input: "bytes=-499", | ||
oracle: {begin: 0, end: 500} | ||
}, | ||
{ | ||
description: "The final 500 bytes (byte offsets 9500-9999, inclusive)", | ||
description: "The final 500 bytes with final elided (byte offsets 9500-9999, inclusive)", | ||
input: "bytes=9500-", | ||
@@ -30,7 +30,2 @@ oracle: {begin: 9500, end: 10000} | ||
{ | ||
description: "The first and last bytes only (bytes 0 and 9999)", | ||
input: "bytes=0-0,-1", | ||
oracle: {begin: 0, end: 1} | ||
}, | ||
{ | ||
description: "Legal but not canonical specification of the second 500 bytes (byte offsets 500-999, inclusive)", | ||
@@ -37,0 +32,0 @@ input: "bytes=500-600,601-999", |
@@ -7,35 +7,181 @@ | ||
describe("http client and server apps", function () { | ||
// http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.35.1 | ||
describe("HTTP Range", function () { | ||
var fixture, app, serveAndTest; | ||
describe("Byte Ranges" , function () { | ||
beforeEach(function () { | ||
fixture = FS.join(module.directory || __dirname, "fixtures", "01234.txt"); | ||
app = new Apps.Chain() | ||
.use(Apps.Cap) | ||
.use(function () { | ||
return Apps.File(fixture); | ||
}) | ||
.end(); | ||
serveAndTest = function serveAndTest(rangeExp, expectedStatus) { | ||
return Http.Server(app) | ||
.listen(0) | ||
.then(function (server) { | ||
var port = server.node.address().port; | ||
return Http.read({ | ||
"url": "http://127.0.0.1:" + port + "/", | ||
"headers": { | ||
"range": rangeExp | ||
} | ||
}, function (response) { | ||
return response.status === expectedStatus; | ||
}).finally(server.stop); | ||
}) | ||
} | ||
}); | ||
it("should read a partial range", function () { | ||
// A byte range operation MAY specify a single range of bytes, or a set of ranges within a single entity. | ||
// ranges-specifier = byte-ranges-specifier | ||
// byte-ranges-specifier = bytes-unit "=" byte-range-set | ||
// byte-range-set = 1#( byte-range-spec | suffix-byte-range-spec ) | ||
// byte-range-spec = first-byte-pos "-" [last-byte-pos] | ||
// first-byte-pos = 1*DIGIT | ||
// last-byte-pos = 1*DIGIT | ||
describe("byte range spec", function () { | ||
// The first-byte-pos value in a byte-range-spec gives the byte-offset of the first byte in a range. The | ||
// last-byte-pos value gives the byte-offset of the last byte in the range; that is, the byte positions | ||
// specified are inclusive. Byte offsets start at zero. | ||
it("positions are inclusive", function () { | ||
return serveAndTest("bytes=0-2", 206) | ||
.then(function (content) { | ||
expect(content.toString('utf-8')).toEqual('012'); | ||
}); | ||
}); | ||
it("last position is optional", function () { | ||
return serveAndTest("bytes=0-", 206) | ||
.then(function (content) { | ||
expect(content.toString('utf-8')).toEqual('01234.txt\n'); | ||
}); | ||
}); | ||
var fixture = FS.join(module.directory || __dirname, "fixtures", "1234.txt"); | ||
// TODO | ||
xit("non contiguous ranges", function () { | ||
return serveAndTest("bytes=0-2,4-5", 206) | ||
.then(function (content) { | ||
expect(content.toString('utf-8')).toEqual('0124.'); | ||
}); | ||
}); | ||
var app = new Apps.Chain() | ||
.use(Apps.Cap) | ||
.use(function () { | ||
return Apps.File(fixture); | ||
}) | ||
.end() | ||
// If the last-byte-pos value is present, it MUST be greater than | ||
// or equal to the first-byte-pos in that byte-range-spec, or the | ||
// byte- range-spec is syntactically invalid. The recipient of a | ||
// byte-range- set that includes one or more syntactically invalid | ||
// byte-range-spec values MUST ignore the header field that | ||
// includes that byte-range- set. | ||
describe("invalid syntax should be ignored", function () { | ||
it("last positions must be greater than first", function () { | ||
return serveAndTest("bytes=4-3", 216) | ||
.then(function (content) { | ||
expect(false).toBe(true); | ||
}, function (error) { | ||
expect(error.response.status).toBe(416); | ||
}) | ||
}); | ||
it("if any part is invalid, all of it is invalid", function () { | ||
return serveAndTest("bytes=0-2,4-3", 216) | ||
.then(function (content) { | ||
expect(false).toBe(true); | ||
}, function (error) { | ||
expect(error.response.status).toBe(416); | ||
}); | ||
}); | ||
}); | ||
return Http.Server(app) | ||
.listen(0) | ||
.then(function (server) { | ||
var port = server.node.address().port; | ||
return Http.read({ | ||
"url": "http://127.0.0.1:" + port + "/", | ||
"headers": { | ||
"range": "bytes=1-2" | ||
} | ||
}, function (response) { | ||
return response.status === 206; | ||
}) | ||
.then(function (content) { | ||
expect(content.toString('utf-8')).toEqual('23'); | ||
}) | ||
.finally(server.stop) | ||
}) | ||
// If the last-byte-pos value is absent, or if the value is greater than or equal to the current length of | ||
// the entity-body, last-byte-pos is taken to be equal to one less than the current length of the entity- | ||
// body in bytes. | ||
describe("last position should be truncated to the length of the content", function () { | ||
it("single range", function () { | ||
return serveAndTest("bytes=0-10", 206) | ||
.then(function (content) { | ||
expect(content.toString('utf-8')).toEqual('01234.txt\n'); | ||
}); | ||
}); | ||
// TODO | ||
xit("multiple ranges", function () { | ||
return serveAndTest("bytes=0-2,1-10", 206) | ||
.then(function (content) { | ||
expect(content.toString('utf-8')).toEqual('0121234.txt\n'); | ||
}); | ||
}); | ||
}); | ||
}); | ||
//By its choice of last-byte-pos, a client can limit the number of | ||
//bytes retrieved without knowing the size of | ||
// the entity. | ||
// suffix-byte-range-spec = "-" suffix-length | ||
// suffix-length = 1*DIGIT | ||
describe("suffix byte range spec", function () { | ||
it("can limit the number of bytes retrieved", function () { | ||
return serveAndTest("bytes=-3", 206) | ||
.then(function (content) { | ||
expect(content.toString('utf-8')).toEqual('0123'); | ||
}); | ||
}); | ||
// A suffix-byte-range-spec is used to specify the suffix of the | ||
// entity-body, of a length given by the suffix-length value. (That | ||
// is, this form specifies the last N bytes of an entity-body.) If | ||
// the entity is shorter than the specified suffix-length, the | ||
// entire entity-body is used. | ||
it("last position should be truncated to the length", function () { | ||
return serveAndTest("bytes=-20", 206) | ||
.then(function (content) { | ||
expect(content.toString('utf-8')).toEqual('01234.txt\n'); | ||
}); | ||
}); | ||
}); | ||
// If a syntactically valid byte-range-set includes at least one byte- | ||
// range-spec whose first-byte-pos is less than the current length of | ||
// the entity-body, or at least one suffix-byte-range-spec with a non- | ||
// zero suffix-length, then the byte-range-set is satisfiable. | ||
// Otherwise, the byte-range-set is unsatisfiable. If the | ||
// byte-range-set is unsatisfiable, the server SHOULD return a response | ||
// with a status of 416 (Requested range not satisfiable). Otherwise, | ||
// the server SHOULD return a response with a status of 206 (Partial | ||
// Content) containing the satisfiable ranges of the entity-body. | ||
describe("satisfiability", function () { | ||
it("should return 416 if a byte range spec is unsatisfiable", function () { | ||
return serveAndTest("bytes=10-11", 416) | ||
.then(function (content) { | ||
expect(true).toBeTruthy(); | ||
}).fail(function () { | ||
expect(false).toBeTruthy(); | ||
}); | ||
}); | ||
it("should return 416 if a suffix byte range spec is unsatisfiable", function () { | ||
return serveAndTest("bytes=1-0", 206) | ||
.then(function (content) { | ||
expect(true).toBeFalsy(); | ||
}).fail(function (error) { | ||
expect(error.response.status).toBe(416); | ||
}); | ||
}); | ||
it("should return 416 if all byte range spec are unsatisfiable", function () { | ||
return serveAndTest("bytes=10-11,-0", 416) | ||
.then(function (content) { | ||
expect(true).toBeTruthy(); | ||
}).fail(function () { | ||
expect(false).toBeTruthy(); | ||
}); | ||
}); | ||
// TODO | ||
xit("should return 200 if at least one byte range spec is satisfiable", function () { | ||
return serveAndTest("bytes=10-11,0-2", 200) | ||
.then(function (content) { | ||
expect(content.toString('utf-8')).toEqual('012'); | ||
}); | ||
}); | ||
}); | ||
}); | ||
}); | ||
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
237257
74
5966