Browse Source

Use "url" module instead of "uri" module in http.js.

Deprecate the URI module and remove tests for it.
- Rename "uri" to "url".
- Use the "url" module instead of the "uri" module.
- Remove the url parsing from http.js
- Update http.cat with the changed field names.
- Update tests for changes to http.js
- Update documentation for changes in http.js
v0.7.4-release
isaacs 15 years ago
committed by Ryan Dahl
parent
commit
2b3d9e4ad0
  1. 41
      doc/api.txt
  2. 64
      lib/http.js
  3. 244
      lib/uri.js
  4. 3
      test/mjsunit/test-http-client-race.js
  5. 3
      test/mjsunit/test-http-malformed-request.js
  6. 3
      test/mjsunit/test-http-proxy.js
  7. 12
      test/mjsunit/test-http-server.js
  8. 7
      test/mjsunit/test-http-tls.js
  9. 7
      test/mjsunit/test-http.js
  10. 3
      test/mjsunit/test-remote-module-loading.js
  11. 193
      test/mjsunit/test-uri.js

41
doc/api.txt

@ -818,8 +818,8 @@ The request method as a string. Read only. Example:
+"GET"+, +"DELETE"+.
+request.uri+ ::
Request URI Object. This contains only the parameters that are
+request.url+ ::
Request URL string. This contains only the URL that is
present in the actual HTTP request. If the request is
+
----------------------------------------
@ -828,16 +828,41 @@ Accept: text/plain\r\n
\r\n
----------------------------------------
+
Then +request.uri+ will be
Then +request.url+ will be
+
----------------------------------------
{ full: "/status?name=ryan",
path: "/status",
queryString: "name=ryan",
params: { "name": "ryan" },
fragment: ""
"/status?name=ryan"
----------------------------------------
+
If you would like to parse the URL into its parts, you can use
+require("url").parse(request.url)+. Example:
+
----------------------------------------
node> require("url").parse("/status?name=ryan")
{
"href": "/status?name=ryan",
"search": "?name=ryan",
"query": "name=ryan",
"pathname": "/status"
}
----------------------------------------
+
If you would like to extract the params from the query string,
you can use the +require("querystring").parse+ function, or pass
+true+ as the second argument to +require("url").parse+. Example:
+
----------------------------------------
node> require("url").parse("/status?name=ryan", true)
{
"href": "/status?name=ryan",
"search": "?name=ryan",
"query": {
"name": "ryan"
},
"pathname": "/status"
}
----------------------------------------
+
+request.headers+ ::

64
lib/http.js

@ -57,13 +57,7 @@ function IncomingMessage (connection) {
this.headers = {};
// request (server) only
this.uri = {
full: "",
queryString: "",
fragment: "",
path: "",
params: {}
};
this.url = "";
this.method = null;
@ -74,20 +68,8 @@ function IncomingMessage (connection) {
sys.inherits(IncomingMessage, process.EventEmitter);
exports.IncomingMessage = IncomingMessage;
var decode = require("uri").decode;
IncomingMessage.prototype._parseQueryString = function () {
var parts = this.uri.queryString.split('&');
for (var j = 0; j < parts.length; j++) {
var i = parts[j].indexOf('=');
if (i < 0) continue;
try {
var key = decode(parts[j].slice(0,i))
var value = decode(parts[j].slice(i+1));
this.uri.params[key] = value;
} catch (e) {
continue;
}
}
throw new Error("_parseQueryString is deprecated. Use require(\"querystring\") to parse query strings.\n");
};
IncomingMessage.prototype.setBodyEncoding = function (enc) {
@ -267,7 +249,7 @@ ServerResponse.prototype.sendHeader = function (statusCode, headers) {
};
function ClientRequest (method, uri, headers) {
function ClientRequest (method, url, headers) {
OutgoingMessage.call(this);
this.should_keep_alive = false;
@ -278,7 +260,7 @@ function ClientRequest (method, uri, headers) {
}
this.closeOnFinish = true;
this.sendHeaderLines(method + " " + uri + " HTTP/1.1\r\n", headers);
this.sendHeaderLines(method + " " + url + " HTTP/1.1\r\n", headers);
}
sys.inherits(ClientRequest, OutgoingMessage);
exports.ClientRequest = ClientRequest;
@ -302,21 +284,9 @@ function createIncomingMessageStream (connection, incoming_listener) {
value = null;
});
// Only servers will get URI events.
// Only servers will get URL events.
connection.addListener("url", function (data) {
incoming.uri.full += data;
});
connection.addListener("path", function (data) {
incoming.uri.path += data;
});
connection.addListener("fragment", function (data) {
incoming.uri.fragment += data;
});
connection.addListener("queryString", function (data) {
incoming.uri.queryString += data;
incoming.url += data;
});
connection.addListener("headerField", function (data) {
@ -352,10 +322,6 @@ function createIncomingMessageStream (connection, incoming_listener) {
if (info.method) {
// server only
incoming.method = info.method;
if (incoming.uri.queryString.length > 0) {
incoming._parseQueryString();
}
} else {
// client only
incoming.statusCode = info.statusCode;
@ -548,13 +514,13 @@ process.http.Client.prototype.put = function () {
throw new Error("client.put(...) is now client.request('PUT', ...)");
};
process.http.Client.prototype.request = function (method, uri, headers) {
if (typeof(uri) != "string") { // assume method was omitted, shift arguments
headers = uri;
uri = method;
process.http.Client.prototype.request = function (method, url, headers) {
if (typeof(url) != "string") { // assume method was omitted, shift arguments
headers = url;
url = method;
method = null;
}
var req = new ClientRequest(method || "GET", uri, headers);
var req = new ClientRequest(method || "GET", url, headers);
this._pushRequest(req);
return req;
};
@ -565,7 +531,7 @@ exports.cat = function (url, encoding, headers) {
encoding = encoding || "utf8";
var uri = require("uri").parse(url);
var url = require("url").parse(url);
headers = headers || {};
var hasHost = false;
@ -574,11 +540,11 @@ exports.cat = function (url, encoding, headers) {
break;
}
if (!hasHost) {
headers["Host"] = uri.domain;
headers["Host"] = url.hostname;
}
var client = exports.createClient(uri.port || 80, uri.domain);
var req = client.request(uri.path || "/", headers);
var client = exports.createClient(url.port || 80, url.hostname);
var req = client.request((url.pathname || "/")+(url.search || "")+(url.hash || ""), headers);
client.addListener("error", function () {
promise.emitError();

244
lib/uri.js

@ -1,243 +1,5 @@
/**
* uri.js
* A URI parser, compliant with assorted RFCs, providing parsing and resolution utilities.
**/
process.stdio.writeError("\nWARNING: uri module is deprecated. Please use require(\"url\") instead.\n");
/*
Blatantly stolen with permission from Narwhal, which did the same from Chiron,
and bits taken from parseUri 1.2.1 (c) 2007 Steven Levithan <stevenlevithan.com> MIT License,
and probably also plagiarizing http://code.google.com/p/js-uri/ in some ways.
Most lines have been changed, please don't blame any of the above persons for
any errors in this file.
*/
exports.parse = uri_parse;
exports.format = uri_format;
exports.resolve = uri_resolve;
exports.resolveObject = uri_resolveObject;
exports.decode = uri_decode;
/****
Decode a URI, replacing + with space
*/
function uri_decode (s) {
return decodeURIComponent(s.replace(/\+/g, ' '));
}
/**** expressionKeys
members of a parsed URI object that you get
from evaluting the strict regular expression.
*/
var expressionKeys = [
"url",
"protocol",
"authorityRoot",
"authority",
"userInfo",
"user",
"password",
"domain",
"port",
"path",
"root",
"directory",
"file",
"query",
"anchor"
],
strictExpression = new RegExp( /* url */
"^" +
"(?:" +
"([^:/?#]+):" + /* protocol */
")?" +
"(?:" +
"(//)" + /* authorityRoot */
"(" + /* authority */
"(?:" +
"(" + /* userInfo */
"([^:@/]*)" + /* user */
":?" +
"([^@/]*)" + /* password */
")?" +
"@" +
")?" +
"([^:/?#]*)" + /* domain */
"(?::(\\d*))?" + /* port */
")" +
")?" +
"(" + /* path */
"(/?)" + /* root */
"((?:[^?#/]*/)*)" +
"([^?#]*)" + /* file */
")" +
"(?:\\?([^#]*))?" + /* query */
"(?:#(.*))?" /*anchor */
);
/**** parse
a URI parser function that uses the `strictExpression`
and `expressionKeys` and returns an `Object`
mapping all `keys` to values.
*/
function uri_parse (url) {
var items = {},
parts = strictExpression.exec(url);
for (var i = 0; i < parts.length; i++) {
items[expressionKeys[i]] = parts[i] ? parts[i] : "";
}
items.root = (items.root || items.authorityRoot) ? '/' : '';
items.directories = items.directory.split("/");
if (items.directories[items.directories.length - 1] == "") {
items.directories.pop();
}
/* normalize */
items.directories = require("path").normalizeArray(items.directories);
items.domains = items.domain.split(".");
return items;
};
/**** format
accepts a parsed URI object and returns
the corresponding string.
*/
function uri_format (object) {
if (typeof(object) == 'undefined')
throw new Error("UrlError: URL undefined for urls#format");
if (object instanceof String || typeof(object) === 'string')
return object;
var domain =
object.domains ?
object.domains.join(".") :
object.domain;
var userInfo = (
object.user ||
object.password
) ? (
(object.user || "") +
(object.password ? ":" + object.password : "")
) :
object.userInfo;
var authority = object.authority || ((
userInfo ||
domain ||
object.port
) ? (
(userInfo ? userInfo + "@" : "") +
(domain || "") +
(object.port ? ":" + object.port : "")
) : "");
var directory =
object.directories ?
object.directories.join("/") :
object.directory;
var path =
object.path ? object.path.substr(1) : (
(directory || object.file) ? (
(directory ? directory + "/" : "") +
(object.file || "")
) : "");
var authorityRoot =
object.authorityRoot
|| authority ? "//" : "";
return object.url = ((
(object.protocol ? object.protocol + ":" : "") +
(authorityRoot) +
(authority) +
(object.root || (authority && path) ? "/" : "") +
(path ? path : "") +
(object.query ? "?" + object.query : "") +
(object.anchor ? "#" + object.anchor : "")
) || object.url || "");
exports.decode = exports.parse = exports.format = exports.resolveObject = exports.resolve = function () {
throw new Error("uri module is deprecated. Please use require(\"url\") instead.");
};
function uri_resolveObject (source, relative) {
if (!source) return relative;
// parse a string, or get new objects
source = uri_parse(uri_format(source));
relative = uri_parse(uri_format(relative));
if (relative.url === "") return source;
// links to xyz:... from abc:... are always absolute.
if (relative.protocol && source.protocol && relative.protocol !== source.protocol) {
return relative;
}
// if there's an authority root, but no protocol, then keep the current protocol
if (relative.authorityRoot && !relative.protocol) {
relative.protocol = source.protocol;
}
// if we have an authority root, then it's absolute
if (relative.authorityRoot) return relative;
// at this point, we start doing the tricky stuff
// we know that relative doesn't have an authority, but might have root,
// path, file, query, etc.
// also, the directory segments might contain .. or .
// start mutating "source", and then return that.
// relative urls that start with / are absolute to the authority/protocol
if (relative.root) {
[
"path", "root", "directory", "directories", "file", "query", "anchor"
].forEach(function (part) { source[part] = relative[part] });
} else {
// if you have /a/b/c and you're going to x/y/z, then that's /a/b/x/y/z
// if you have /a/b/c/ and you're going ot x/y/z, then that's /a/b/c/x/y/z
// if you have /a/b/c and you're going to ?foo, then that's /a/b/c?foo
if (relative.file || relative.directory) {
source.file = relative.file;
source.query = relative.query;
source.anchor = relative.anchor;
}
if (relative.query) source.query = relative.query;
if (relative.query || relative.anchor) source.anchor = relative.anchor;
// just append the dirs. we'll sort out .. and . later
source.directories = source.directories.concat(relative.directories);
}
// back up "file" to the first non-dot
// one step for ., two for ..
var file = source.file;
while (file === ".." || file === ".") {
if (file === "..") source.directories.pop();
file = source.directories.pop();
}
source.file = file || "";
// walk over the dirs, replacing a/b/c/.. with a/b/ and a/b/c/. with a/b/c
var dirs = [];
source.directories.forEach(function (dir, i) {
if (dir === "..") dirs.pop();
else if (dir !== "." && dir !== "") dirs.push(dir);
});
// now construct path/etc.
source.directories = dirs;
source.directory = dirs.concat("").join("/");
source.path = source.root + source.directory + source.file;
source.url = uri_format(source);
return source;
};
/**** resolve
returns a URL resovled to a relative URL from a source URL.
*/
function uri_resolve (source, relative) {
return uri_format(uri_resolveObject(source, relative));
};

3
test/mjsunit/test-http-client-race.js

@ -1,12 +1,13 @@
process.mixin(require("./common"));
http = require("http");
url = require("url");
PORT = 8888;
var body1_s = "1111111111111111";
var body2_s = "22222";
var server = http.createServer(function (req, res) {
var body = req.uri.path === "/1" ? body1_s : body2_s;
var body = url.parse(req.url).pathname === "/1" ? body1_s : body2_s;
res.sendHeader(200, { "Content-Type": "text/plain"
, "Content-Length": body.length
});

3
test/mjsunit/test-http-malformed-request.js

@ -1,6 +1,7 @@
process.mixin(require("./common"));
tcp = require("tcp");
http = require("http");
url = require("url");
// Make sure no exceptions are thrown when receiving malformed HTTP
// requests.
@ -10,7 +11,7 @@ nrequests_completed = 0;
nrequests_expected = 1;
var s = http.createServer(function (req, res) {
puts("req: " + JSON.stringify(req.uri));
puts("req: " + JSON.stringify(url.parse(req.url)));
res.sendHeader(200, {"Content-Type": "text/plain"});
res.sendBody("Hello World");

3
test/mjsunit/test-http-proxy.js

@ -1,5 +1,6 @@
process.mixin(require("./common"));
http = require("http");
url = require("url");
var PROXY_PORT = 8869;
var BACKEND_PORT = 8870;
@ -16,7 +17,7 @@ backend.listen(BACKEND_PORT);
var proxy_client = http.createClient(BACKEND_PORT);
var proxy = http.createServer(function (req, res) {
debug("proxy req headers: " + JSON.stringify(req.headers));
var proxy_req = proxy_client.request(req.uri.path);
var proxy_req = proxy_client.request(url.parse(req.url).pathname);
proxy_req.finish(function(proxy_res) {
res.sendHeader(proxy_res.statusCode, proxy_res.headers);
proxy_res.addListener("body", function(chunk) {

12
test/mjsunit/test-http-server.js

@ -1,6 +1,8 @@
process.mixin(require("./common"));
tcp = require("tcp");
http = require("http");
url = require("url");
qs = require("querystring");
var port = 8222;
@ -15,14 +17,14 @@ http.createServer(function (req, res) {
if (req.id == 0) {
assert.equal("GET", req.method);
assert.equal("/hello", req.uri.path);
assert.equal("world", req.uri.params["hello"]);
assert.equal("b==ar", req.uri.params["foo"]);
assert.equal("/hello", url.parse(req.url).pathname);
assert.equal("world", qs.parse(url.parse(req.url).query).hello);
assert.equal("b==ar", qs.parse(url.parse(req.url).query).foo);
}
if (req.id == 1) {
assert.equal("POST", req.method);
assert.equal("/quit", req.uri.path);
assert.equal("/quit", url.parse(req.url).pathname);
}
if (req.id == 2) {
@ -37,7 +39,7 @@ http.createServer(function (req, res) {
setTimeout(function () {
res.sendHeader(200, {"Content-Type": "text/plain"});
res.sendBody(req.uri.path);
res.sendBody(url.parse(req.url).pathname);
res.finish();
}, 1);

7
test/mjsunit/test-http-tls.js

@ -1,5 +1,6 @@
process.mixin(require("./common"));
http = require("http");
url = require("url");
PORT = 8888;
HOST = "localhost";
@ -34,7 +35,7 @@ var http_server=http.createServer(function (req, res) {
if (responses_sent == 0) {
assert.equal("GET", req.method);
assert.equal("/hello", req.uri.path);
assert.equal("/hello", url.parse(req.url).pathname);
p(req.headers);
assert.equal(true, "accept" in req.headers);
@ -46,13 +47,13 @@ var http_server=http.createServer(function (req, res) {
if (responses_sent == 1) {
assert.equal("POST", req.method);
assert.equal("/world", req.uri.path);
assert.equal("/world", url.parse(req.url).pathname);
this.close();
}
req.addListener("complete", function () {
res.sendHeader(200, {"Content-Type": "text/plain"});
res.sendBody("The path was " + req.uri.path);
res.sendBody("The path was " + url.parse(req.url).pathname);
res.finish();
responses_sent += 1;
});

7
test/mjsunit/test-http.js

@ -1,5 +1,6 @@
process.mixin(require("./common"));
http = require("http");
url = require("url");
PORT = 8888;
var responses_sent = 0;
@ -10,7 +11,7 @@ var body1 = "";
http.createServer(function (req, res) {
if (responses_sent == 0) {
assert.equal("GET", req.method);
assert.equal("/hello", req.uri.path);
assert.equal("/hello", url.parse(req.url).pathname);
p(req.headers);
assert.equal(true, "accept" in req.headers);
@ -22,13 +23,13 @@ http.createServer(function (req, res) {
if (responses_sent == 1) {
assert.equal("POST", req.method);
assert.equal("/world", req.uri.path);
assert.equal("/world", url.parse(req.url).pathname);
this.close();
}
req.addListener("complete", function () {
res.sendHeader(200, {"Content-Type": "text/plain"});
res.sendBody("The path was " + req.uri.path);
res.sendBody("The path was " + url.parse(req.url).pathname);
res.finish();
responses_sent += 1;
});

3
test/mjsunit/test-remote-module-loading.js

@ -3,11 +3,12 @@ process.mixin(require("./common"));
var PORT = 8889;
var http = require('http');
var sys = require('sys');
var url = require("url");
var modulesLoaded = 0;
var server = http.createServer(function(req, res) {
var body = 'exports.httpPath = function() {'+
'return '+JSON.stringify(req.uri.path)+';'+
'return '+JSON.stringify(url.parse(req.url).pathname)+';'+
'};';
res.sendHeader(200, {'Content-Type': 'text/javascript'});

193
test/mjsunit/test-uri.js

@ -1,193 +0,0 @@
process.mixin(require("./common"));
var uri = require("uri"),
sys = require("sys");
// URIs to parse, and expected data
// { url : parsed }
var parseTests = {
"http://www.narwhaljs.org/blog/categories?id=news" : {
"url": "http://www.narwhaljs.org/blog/categories?id=news",
"protocol": "http",
"authorityRoot": "//",
"authority": "www.narwhaljs.org",
"userInfo": "",
"user": "",
"password": "",
"domain": "www.narwhaljs.org",
"port": "",
"path": "/blog/categories",
"root": "/",
"directory": "blog/",
"file": "categories",
"query": "id=news",
"anchor": "",
"directories": [
"blog"
],
"domains": [
"www",
"narwhaljs",
"org"
]
},
"http://mt0.google.com/vt/lyrs=m@114&hl=en&src=api&x=2&y=2&z=3&s=" : {
"url": "http://mt0.google.com/vt/lyrs=m@114&hl=en&src=api&x=2&y=2&z=3&s=",
"protocol": "http",
"authorityRoot": "//",
"authority": "mt0.google.com",
"userInfo": "",
"user": "",
"password": "",
"domain": "mt0.google.com",
"port": "",
"path": "/vt/lyrs=m@114&hl=en&src=api&x=2&y=2&z=3&s=",
"root": "/",
"directory": "vt/",
"file": "lyrs=m@114&hl=en&src=api&x=2&y=2&z=3&s=",
"query": "",
"anchor": "",
"directories": [
"vt"
],
"domains": [
"mt0",
"google",
"com"
]
},
"http://mt0.google.com/vt/lyrs=m@114???&hl=en&src=api&x=2&y=2&z=3&s=" : {
"url": "http://mt0.google.com/vt/lyrs=m@114???&hl=en&src=api&x=2&y=2&z=3&s=",
"protocol": "http",
"authorityRoot": "//",
"authority": "mt0.google.com",
"userInfo": "",
"user": "",
"password": "",
"domain": "mt0.google.com",
"port": "",
"path": "/vt/lyrs=m@114",
"root": "/",
"directory": "vt/",
"file": "lyrs=m@114",
"query": "??&hl=en&src=api&x=2&y=2&z=3&s=",
"anchor": "",
"directories": [
"vt"
],
"domains": [
"mt0",
"google",
"com"
]
},
"http://user:pass@mt0.google.com/vt/lyrs=m@114???&hl=en&src=api&x=2&y=2&z=3&s=" : {
"url": "http://user:pass@mt0.google.com/vt/lyrs=m@114???&hl=en&src=api&x=2&y=2&z=3&s=",
"protocol": "http",
"authorityRoot": "//",
"authority": "user:pass@mt0.google.com",
"userInfo": "user:pass",
"user": "user",
"password": "pass",
"domain": "mt0.google.com",
"port": "",
"path": "/vt/lyrs=m@114",
"root": "/",
"directory": "vt/",
"file": "lyrs=m@114",
"query": "??&hl=en&src=api&x=2&y=2&z=3&s=",
"anchor": "",
"directories": [
"vt"
],
"domains": [
"mt0",
"google",
"com"
]
},
"file:///etc/passwd" : {
"url": "file:///etc/passwd",
"protocol": "file",
"authorityRoot": "//",
"authority": "",
"userInfo": "",
"user": "",
"password": "",
"domain": "",
"port": "",
"path": "/etc/passwd",
"root": "/",
"directory": "etc/",
"file": "passwd",
"query": "",
"anchor": "",
"directories": [
"etc"
],
"domains": [
""
]
},
"file:///etc/node/" : {
"url": "file:///etc/node/",
"protocol": "file",
"authorityRoot": "//",
"authority": "",
"userInfo": "",
"user": "",
"password": "",
"domain": "",
"port": "",
"path": "/etc/node/",
"root": "/",
"directory": "etc/node/",
"file": "",
"query": "",
"anchor": "",
"directories": [
"etc",
"node"
],
"domains": [
""
]
}
};
for (var url in parseTests) {
var actual = uri.parse(url),
expected = parseTests[url];
for (var i in expected) {
// sys.debug(i);
// sys.debug("expect: " + JSON.stringify(expected[i]));
// sys.debug("actual: " + JSON.stringify(actual[i]));
var e = JSON.stringify(expected[i]),
a = JSON.stringify(actual[i]);
assert.equal(e, a, "parse(" + url + ")."+i+" == "+e+"\nactual: "+a);
}
var expected = url,
actual = uri.format(parseTests[url]);
assert.equal(expected, actual, "format("+url+") == "+url+"\nactual:"+actual);
}
[
// [from, path, expected]
["/foo/bar/baz", "quux", "/foo/bar/quux"],
["/foo/bar/baz", "quux/asdf", "/foo/bar/quux/asdf"],
["/foo/bar/baz", "quux/baz", "/foo/bar/quux/baz"],
["/foo/bar/baz", "../quux/baz", "/foo/quux/baz"],
["/foo/bar/baz", "/bar", "/bar"],
["/foo/bar/baz/", "quux", "/foo/bar/baz/quux"],
["/foo/bar/baz/", "quux/baz", "/foo/bar/baz/quux/baz"],
["/foo/bar/baz", "../../../../../../../../quux/baz", "/quux/baz"],
["/foo/bar/baz", "../../../../../../../quux/baz", "/quux/baz"]
].forEach(function (relativeTest) {
var a = uri.resolve(relativeTest[0], relativeTest[1]),
e = relativeTest[2];
assert.equal(e, a,
"resolve("+[relativeTest[0], relativeTest[1]]+") == "+e+
"\n actual="+a);
});
Loading…
Cancel
Save