Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(node/url): Add url.urlToHttpOptions(url) #1426

Merged
merged 2 commits into from
Oct 19, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion node/_tools/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@
"test-net-server-call-listen-multiple-times.js",
"test-net-server-try-ports.js",
"test-net-write-arguments.js",
"test-url-format-whatwg.js"
"test-url-format-whatwg.js",
"test-url-urltooptions.js"
],
"pummel": [
"test-net-write-callbacks.js"
Expand Down Expand Up @@ -147,6 +148,7 @@
"test-url-fileurltopath.js",
"test-url-format-whatwg.js",
"test-url-pathtofileurl.js",
"test-url-urltooptions.js",
"test-util-inherits.js"
],
"pummel": [
Expand Down
45 changes: 45 additions & 0 deletions node/_tools/suites/parallel/test-url-urltooptions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// deno-fmt-ignore-file
// deno-lint-ignore-file

// Copyright Joyent and Node contributors. All rights reserved. MIT license.
// Taken from Node 16.11.1
// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually

'use strict';
require('../common');
const assert = require('assert');
const { urlToHttpOptions } = require('url');

// Test urlToHttpOptions
const urlObj = new URL('http://user:[email protected]:21/aaa/zzz?l=24#test');
const opts = urlToHttpOptions(urlObj);
assert.strictEqual(opts instanceof URL, false);
assert.strictEqual(opts.protocol, 'http:');
assert.strictEqual(opts.auth, 'user:pass');
assert.strictEqual(opts.hostname, 'foo.bar.com');
assert.strictEqual(opts.port, 21);
assert.strictEqual(opts.path, '/aaa/zzz?l=24');
assert.strictEqual(opts.pathname, '/aaa/zzz');
assert.strictEqual(opts.search, '?l=24');
assert.strictEqual(opts.hash, '#test');

const { hostname } = urlToHttpOptions(new URL('http://[::1]:21'));
assert.strictEqual(hostname, '::1');

// If a WHATWG URL object is copied, it is possible that the resulting copy
// contains the Symbols that Node uses for brand checking, but not the data
// properties, which are getters. Verify that urlToHttpOptions() can handle
// such a case.
const copiedUrlObj = { ...urlObj };
const copiedOpts = urlToHttpOptions(copiedUrlObj);
assert.strictEqual(copiedOpts instanceof URL, false);
assert.strictEqual(copiedOpts.protocol, undefined);
assert.strictEqual(copiedOpts.auth, undefined);
assert.strictEqual(copiedOpts.hostname, undefined);
// TODO(wafuwafu13): Fix `AssertionError: Values have the same structure but are not reference-equal`
// assert.strictEqual(copiedOpts.port, NaN);
assert.strictEqual(copiedOpts.path, '');
assert.strictEqual(copiedOpts.pathname, undefined);
assert.strictEqual(copiedOpts.search, undefined);
assert.strictEqual(copiedOpts.hash, undefined);
assert.strictEqual(copiedOpts.href, undefined);
51 changes: 51 additions & 0 deletions node/url.ts
Original file line number Diff line number Diff line change
Expand Up @@ -265,9 +265,60 @@ export function pathToFileURL(filepath: string): URL {
return outURL;
}

interface HttpOptions {
protocol: string;
hostname: string;
hash: string;
search: string;
pathname: string;
path: string;
href: string;
port?: number;
auth?: string;
}

/**
* This utility function converts a URL object into an ordinary options object as expected by the `http.request()` and `https.request()` APIs.
* @param url The `WHATWG URL` object to convert to an options object.
* @returns HttpOptions
* @returns HttpOptions.protocol Protocol to use.
* @returns HttpOptions.hostname A domain name or IP address of the server to issue the request to.
* @returns HttpOptions.hash The fragment portion of the URL.
* @returns HttpOptions.search The serialized query portion of the URL.
* @returns HttpOptions.pathname The path portion of the URL.
* @returns HttpOptions.path Request path. Should include query string if any. E.G. `'/index.html?page=12'`. An exception is thrown when the request path contains illegal characters. Currently, only spaces are rejected but that may change in the future.
* @returns HttpOptions.href The serialized URL.
* @returns HttpOptions.port Port of remote server.
* @returns HttpOptions.auth Basic authentication i.e. `'user:password'` to compute an Authorization header.
*/
function urlToHttpOptions(url: URL): HttpOptions {
const options: HttpOptions = {
protocol: url.protocol,
hostname: typeof url.hostname === "string" &&
url.hostname.startsWith("[")
? url.hostname.slice(1, -1)
: url.hostname,
hash: url.hash,
search: url.search,
pathname: url.pathname,
path: `${url.pathname || ""}${url.search || ""}`,
href: url.href,
};
if (url.port !== "") {
options.port = Number(url.port);
}
if (url.username || url.password) {
options.auth = `${decodeURIComponent(url.username)}:${
decodeURIComponent(url.password)
}`;
}
return options;
}

export default {
format,
fileURLToPath,
pathToFileURL,
urlToHttpOptions,
URL,
};