Comparing version 1.11.0 to 1.11.1
<!-- vim:ts=4:sts=4:sw=4:et:tw=60 --> | ||
## 1.11.1 | ||
- Fix to `fs.reroot`, binding this properly (@Stuk) | ||
- `fs.reroot` no longer traverses into directory prefixes implicitly. | ||
We do not expect anyone was depending on this strange behavior, | ||
but do expect folks were not using `reroot` because of it. | ||
- Many missing methods added to RootFs (@3on) | ||
- HTTP request normalization has been greatly improved. | ||
Particularly, the ambiguity of `host` vs `hostname` is resolved. | ||
`host` stands for both the hostname and the port, but only includes the port | ||
if it differs from the default port for the protocol. | ||
HTTP requests will normalize any combination of `host`, `port`, and | ||
`hostname`. | ||
The cookie jar HTTP application adapter has been updated. | ||
- Support for URL authorization has been improved. If you use the `auth` | ||
portion of a URL, it will be implicitly normalized to the corresponding | ||
`Authorization` header, for basic HTTP auth. | ||
Note that the convention in Q-IO is to use lower-case for all input and | ||
output headers, since they are case-insensitive. | ||
- MockFS now handles file modes properly (@Stuk) | ||
- `fs.copyTree` fixed for duplicate file modes (@Stuk) | ||
- Fix for HTTP clients on Node.js 0.11, which changed how it interprets the | ||
`agent` option. | ||
- Fixed a bug, NodeWriter was missing a `node` property, referring back to the | ||
Node.js writable stream. | ||
## 1.11.0 | ||
@@ -4,0 +30,0 @@ |
@@ -141,6 +141,10 @@ var Q = require("q"); | ||
var self = this; | ||
return Q.spread([ | ||
self.open(source, {flags: "rb"}), | ||
self.open(target, {flags: "wb"}) | ||
], function (reader, writer) { | ||
return Q.when(self.stat(source), function (stat) { | ||
var mode = stat.node.mode; | ||
return Q.all([ | ||
self.open(source, {flags: "rb"}), | ||
self.open(target, {flags: "wb", mode: mode}) | ||
]); | ||
}) | ||
.spread(function (reader, writer) { | ||
return Q.when(reader.forEach(function (block) { | ||
@@ -175,3 +179,3 @@ return writer.write(block); | ||
} else { | ||
return Q.when(self.makeDirectory(target), function () { | ||
return Q.when(self.makeDirectory(target, stat.node.mode), function () { | ||
return copySubTree; | ||
@@ -417,14 +421,3 @@ }); | ||
path = path || this.ROOT; | ||
return Q.when(this.list(path), function (list) { | ||
if (list.length !== 1) | ||
return RootFs(self, path); | ||
var nextPath = self.join(path, list[0]); | ||
return Q.when(self.stat(nextPath), function (stat) { | ||
if (stat.isDirectory()) { | ||
return reroot(nextPath); | ||
} else { | ||
return RootFs(self, path); | ||
} | ||
}); | ||
}); | ||
return RootFs(self, path); | ||
} | ||
@@ -431,0 +424,0 @@ |
@@ -97,2 +97,5 @@ | ||
node._entries[base] = new FileNode(this); | ||
if ("mode" in options) { | ||
node._entries[base].mode = options.mode; | ||
} | ||
} | ||
@@ -153,3 +156,3 @@ var fileNode = node._entries[base]._follow(path); | ||
MockFs.prototype.makeDirectory = function (path) { | ||
MockFs.prototype.makeDirectory = function (path, mode) { | ||
var self = this; | ||
@@ -175,2 +178,5 @@ return Q.fcall(function () { | ||
node._entries[name] = new DirectoryNode(self); | ||
if (mode) { | ||
node._entries[name].mode = mode; | ||
} | ||
}); | ||
@@ -211,4 +217,3 @@ }; | ||
path = self.absolute(path); | ||
var node = self._root._walk(path); | ||
return node; | ||
return new self.Stats(self._root._walk(path)); | ||
}); | ||
@@ -265,3 +270,3 @@ }; | ||
path = self.absolute(path); | ||
self._root._walk(path)._follow(path)._mode = mode; | ||
self._root._walk(path)._follow(path).mode = mode; | ||
}); | ||
@@ -366,3 +371,3 @@ }; | ||
this._accessed = this._modified = new Date(); | ||
this._mode = parseInt("0644", 8); | ||
this.mode = parseInt("0644", 8); | ||
this._owner = null; | ||
@@ -469,3 +474,3 @@ } | ||
this._entries = Object.create(null); | ||
this._mode = parseInt("0755", 8); | ||
this.mode = parseInt("0755", 8); | ||
} | ||
@@ -472,0 +477,0 @@ |
@@ -116,2 +116,26 @@ | ||
inner.remove = function (path) { | ||
return attenuate(path).then(function (path) { | ||
return outer.remove(path.outer); | ||
}).catch(function (error) { | ||
throw new Error("Can't remove " + JSON.stringify(path)); | ||
}); | ||
}; | ||
inner.makeTree = function (path) { | ||
return attenuate(path).then(function (path) { | ||
return outer.makeTree(path.outer); | ||
}).catch(function (error) { | ||
throw new Error("Can't make tree " + JSON.stringify(path)); | ||
}); | ||
}; | ||
inner.removeTree = function (path) { | ||
return attenuate(path).then(function (path) { | ||
return outer.removeTree(path.outer); | ||
}).catch(function (error) { | ||
throw new Error("Can't remove tree " + JSON.stringify(path)); | ||
}); | ||
}; | ||
return Q.when(outer.canonical(root), function (_root) { | ||
@@ -118,0 +142,0 @@ root = _root; |
var Q = require("q"); | ||
var Cookie = require("../http-cookie"); | ||
Q.longStackSupport = true; | ||
@@ -9,2 +10,5 @@ exports.CookieJar = function (app) { | ||
if (!request.headers.host) { | ||
throw new Error("Requests must have a host header"); | ||
} | ||
var hosts = allHostsContaining(request.headers.host); | ||
@@ -34,4 +38,5 @@ | ||
.map(function (host) { | ||
if (!hostContains(host, request.headers.host)) | ||
if (!hostContains(host, request.headers.host)) { | ||
return []; | ||
} | ||
var pathCookies = hostCookies[host]; | ||
@@ -79,5 +84,6 @@ return concat( | ||
if (response.headers["set-cookie"]) { | ||
var requestHost = ipRe.test(request.headers.host) ? | ||
request.headers.host : | ||
"." + request.headers.host; | ||
var host = request.headers.host; | ||
var hostParts = splitHost(host); | ||
var hostname = hostParts[0]; | ||
var requestHost = ipRe.test(hostname) ? host : "." + host; | ||
// normalize to array | ||
@@ -111,13 +117,26 @@ if (!Array.isArray(response.headers["set-cookie"])) { | ||
var ipRe = /^\d+\.\d+\.\d+\.\d+$/; | ||
var portRe = /^(.*)(:\d+)$/; | ||
function allHostsContaining(content) { | ||
if (ipRe.test(content)) { | ||
return [content]; | ||
} if (content === "localhost") { | ||
return [content]; | ||
function splitHost(host) { | ||
var match = portRe.exec(host); | ||
if (match) { | ||
return [match[1], match[2]]; | ||
} else { | ||
var parts = content.split("."); | ||
return [host, ""]; | ||
} | ||
} | ||
function allHostsContaining(host) { | ||
var parts = splitHost(host); | ||
var hostname = parts[0]; | ||
var port = parts[1]; | ||
if (ipRe.test(hostname)) { | ||
return [hostname + port]; | ||
} if (hostname === "localhost") { | ||
return [hostname + port]; | ||
} else { | ||
var parts = hostname.split("."); | ||
var hosts = []; | ||
while (parts.length > 1) { | ||
hosts.push("." + parts.join(".")); | ||
hosts.push("." + parts.join(".") + port); | ||
parts.shift(); | ||
@@ -129,14 +148,23 @@ } | ||
function hostContains(container, content) { | ||
if (ipRe.test(container) || ipRe.test(content)) { | ||
return container === content; | ||
} else if (/^\./.test(container)) { | ||
function hostContains(containerHost, contentHost) { | ||
var containerParts = splitHost(containerHost); | ||
var containerHostname = containerParts[0]; | ||
var containerPort = containerParts[1]; | ||
var contentParts = splitHost(contentHost); | ||
var contentHostname = contentParts[0]; | ||
var contentPort = contentParts[1]; | ||
if (containerPort !== contentPort) { | ||
return false; | ||
} | ||
if (ipRe.test(containerHostname) || ipRe.test(contentHostname)) { | ||
return containerHostname === contentHostname; | ||
} else if (/^\./.test(containerHostname)) { | ||
return ( | ||
content.lastIndexOf(container) === | ||
content.length - container.length | ||
contentHostname.lastIndexOf(containerHostname) === | ||
contentHostname.length - containerHostname.length | ||
) || ( | ||
container.slice(1) === content | ||
containerHostname.slice(1) === contentHostname | ||
); | ||
} else { | ||
return container === content; | ||
return containerHostname === contentHostname; | ||
} | ||
@@ -143,0 +171,0 @@ }; |
61
http.js
@@ -190,4 +190,5 @@ /** | ||
/*** {String} host */ | ||
request.host = request.hostname + ":" + address.port; | ||
request.port = address.port; | ||
var defaultPort = request.port === (ssl ? 443 : 80); | ||
request.host = request.hostname + (defaultPort ? "" : ":" + request.port); | ||
@@ -233,16 +234,34 @@ var socket = _request.socket; | ||
if (typeof request === "string") { | ||
request = { | ||
url: request | ||
}; | ||
request = {url: request}; | ||
} | ||
request.method = request.method || "GET"; | ||
request.headers = request.headers || {}; | ||
if (request.url) { | ||
var url = URL.parse(request.url); | ||
request.host = url.hostname; | ||
request.port = url.port; | ||
request.ssl = url.protocol === "https:"; | ||
request.method = request.method || "GET"; | ||
request.hostname = url.hostname; | ||
request.host = url.host; | ||
request.port = +url.port; | ||
request.path = (url.pathname || "") + (url.search || ""); | ||
request.headers = request.headers || {}; | ||
request.headers.host = url.hostname; // FIXME name consistency | ||
if (url.auth) { | ||
request.auth = url.auth; | ||
if (!request.headers["authorization"]) { | ||
request.headers["authorization"] = ( | ||
"Basic " + new Buffer(url.auth) | ||
.toString("base64") | ||
); | ||
} | ||
} | ||
} | ||
request.host = request.host || request.headers.host; | ||
request.port = request.port || (request.ssl ? 443 : 80); | ||
if (request.host && !request.hostname) { | ||
request.hostname = request.host.split(":")[0]; | ||
} | ||
if (request.hostname && request.port && !request.host) { | ||
var defaultPort = request.ssl ? 443 : 80; | ||
request.host = request.hostname + (defaultPort ? "" : ":" + request.port); | ||
} | ||
request.headers.host = request.headers.host || request.host; | ||
request.path = request.path || "/"; | ||
return request; | ||
@@ -281,17 +300,17 @@ }; | ||
var deferred = Q.defer(); | ||
var ssl = request.ssl; | ||
var http = ssl ? HTTPS : HTTP; | ||
var http = request.ssl ? HTTPS : HTTP; | ||
var headers = request.headers || {}; | ||
var requestOptions = { | ||
"host": request.hostname, // Node.js quirk | ||
"port": request.port || (request.ssl ? 443 : 80), | ||
"path": request.path || "/", | ||
"method": request.method || "GET", | ||
"headers": request.headers | ||
}; | ||
headers.host = headers.host || request.host; | ||
if (request.agent) { | ||
requestOptions.agent = request.agent; | ||
} | ||
var _request = http.request({ | ||
"host": request.host, | ||
"port": request.port || (ssl ? 443 : 80), | ||
"path": request.path || "/", | ||
"method": request.method || "GET", | ||
"headers": headers, | ||
"agent": request.agent | ||
}, function (_response) { | ||
var _request = http.request(requestOptions, function (_response) { | ||
deferred.resolve(exports.ClientResponse(_response, request.charset)); | ||
@@ -298,0 +317,0 @@ _response.on("error", function (error) { |
{ | ||
"name": "q-io", | ||
"version": "1.11.0", | ||
"version": "1.11.1", | ||
"description": "IO using Q promises", | ||
"homepage": "http://github.com/kriskowal/q-io/", | ||
"author": "Kris Kowal <kris@cixar.com> (http://github.com/kriskowal/)", | ||
"contributors": [ | ||
"Montage Studio", | ||
"Stuart Knightley", | ||
"Jean-romain Prevost", | ||
"Andreas Pizsa (http://github.com/AndreasPizsa/)" | ||
], | ||
"bugs": { | ||
@@ -8,0 +14,0 @@ "mail": "kris@cixar.com", |
@@ -56,3 +56,37 @@ "use strict"; | ||
it("should preserve permissions", function () { | ||
var mock = Mock({ | ||
"a/b": { | ||
"c": { | ||
"d": 66, | ||
"e": 99 | ||
} | ||
} | ||
}); | ||
var mode0777 = parseInt("0777", 8); | ||
var mode0700 = parseInt("0700", 8); | ||
return Q.all([ | ||
mock.chmod("a/b/c/d", mode0777), | ||
mock.chmod("a/b", mode0700), | ||
mock.chmod("a/b/c", mode0700) | ||
]) | ||
.then(function () { | ||
return mock.copyTree("a/b", "a/f"); | ||
}) | ||
.then(function () { | ||
return Q.all([ | ||
mock.stat("a/f/c/d"), | ||
mock.stat("a/f"), | ||
mock.stat("a/f/c") | ||
]); | ||
}) | ||
.spread(function (dStat, fStat, cStat) { | ||
expect(dStat.node.mode & mode0777).toEqual(mode0777); | ||
expect(fStat.node.mode & mode0777).toEqual(mode0700); | ||
expect(cStat.node.mode & mode0777).toEqual(mode0700); | ||
}); | ||
}); | ||
}); | ||
@@ -12,3 +12,3 @@ | ||
hosts.forEach(function (host) { | ||
it("should work on host" + host, function () { | ||
it("should work on host " + host, function () { | ||
@@ -15,0 +15,0 @@ var server = Http.Server(function (request) { |
@@ -94,4 +94,21 @@ | ||
it('should set correct HTTP Basic authentication headers when username and password are passed in the URL',function(){ | ||
request = HTTP.normalizeRequest('http://username:password@www.google.com/'); | ||
expect(request.auth).toBe('username:password'); | ||
expect(request.headers.authorization).toBe('Basic dXNlcm5hbWU6cGFzc3dvcmQ='); | ||
}); | ||
it('should successfully access resources that require HTTP Basic authentication when using the username:password@host.com URL syntax', function(){ | ||
// This tries to access a public resource, see http://test.webdav.org/ | ||
// | ||
// The resource is password protected, but there's no content behind it | ||
// so we will actually receive a 404; that's ok though as at least it's | ||
// a well-defined and expected status. | ||
return HTTP.request('http://user1:user1@test.webdav.org/auth-basic/') | ||
.then(function(response){ | ||
expect(response.status).not.toBe(401); | ||
expect(response.status).toBe(404); | ||
}); | ||
}); | ||
}); | ||
@@ -109,4 +109,6 @@ | ||
self.node = _stream; | ||
return Q(self); // todo returns the begin.promise | ||
} | ||
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
250968
75
6292