Skip to content

Commit 1cbb74d

Browse files
zhangyongshengMylesBorins
zhangyongsheng
authored andcommitted
url: expose urlToHttpOptions utility
PR-URL: #35960 Fixes: #34349 Reviewed-By: James M Snell <[email protected]> Reviewed-By: Michaël Zasso <[email protected]>
1 parent 402f772 commit 1cbb74d

File tree

7 files changed

+95
-46
lines changed

7 files changed

+95
-46
lines changed

doc/api/url.md

+46
Original file line numberDiff line numberDiff line change
@@ -1087,6 +1087,50 @@ new URL('/some/path%.c', 'file:'); // Incorrect: file:///some/path%.c
10871087
pathToFileURL('/some/path%.c'); // Correct: file:///some/path%25.c (POSIX)
10881088
```
10891089
1090+
### `url.urlToHttpOptions(url)`
1091+
<!-- YAML
1092+
added: REPLACEME
1093+
-->
1094+
1095+
* `url` {URL} The [WHATWG URL][] object to convert to an options object.
1096+
* Returns: {Object} Options object
1097+
* `protocol` {string} Protocol to use.
1098+
* `hostname` {string} A domain name or IP address of the server to issue the
1099+
request to.
1100+
* `hash` {string} The fragment portion of the URL.
1101+
* `search` {string} The serialized query portion of the URL.
1102+
* `pathname` {string} The path portion of the URL.
1103+
* `path` {string} Request path. Should include query string if any.
1104+
E.G. `'/index.html?page=12'`. An exception is thrown when the request path
1105+
contains illegal characters. Currently, only spaces are rejected but that
1106+
may change in the future.
1107+
* `href` {string} The serialized URL.
1108+
* `port` {number} Port of remote server.
1109+
* `auth` {string} Basic authentication i.e. `'user:password'` to compute an
1110+
Authorization header.
1111+
1112+
This utility function converts a URL object into an ordinary options object as
1113+
expected by the [`http.request()`][] and [`https.request()`][] APIs.
1114+
1115+
```js
1116+
const { urlToHttpOptions } = require('url');
1117+
const myURL = new URL('https://a:b@測試?abc#foo');
1118+
1119+
console.log(urlToHttpOptions(myUrl));
1120+
/**
1121+
{
1122+
protocol: 'https:',
1123+
hostname: 'xn--g6w251d',
1124+
hash: '#foo',
1125+
search: '?abc',
1126+
pathname: '/',
1127+
path: '/?abc',
1128+
href: 'https://a:b@xn--g6w251d/?abc#foo',
1129+
auth: 'a:b'
1130+
}
1131+
*/
1132+
```
1133+
10901134
## Legacy URL API
10911135
<!-- YAML
10921136
changes:
@@ -1485,6 +1529,8 @@ console.log(myURL.origin);
14851529
[`TypeError`]: errors.md#errors_class_typeerror
14861530
[`URLSearchParams`]: #url_class_urlsearchparams
14871531
[`array.toString()`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/toString
1532+
[`http.request()`]: http.md#http_http_request_options_callback
1533+
[`https.request()`]: https.md#https_https_request_options_callback
14881534
[`new URL()`]: #url_new_url_input_base
14891535
[`querystring`]: querystring.md
14901536
[`require('url').format()`]: #url_url_format_url_options

lib/_http_client.js

+3-3
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ const { OutgoingMessage } = require('_http_outgoing');
5050
const Agent = require('_http_agent');
5151
const { Buffer } = require('buffer');
5252
const { defaultTriggerAsyncIdScope } = require('internal/async_hooks');
53-
const { URL, urlToOptions, searchParamsSymbol } = require('internal/url');
53+
const { URL, urlToHttpOptions, searchParamsSymbol } = require('internal/url');
5454
const { kOutHeaders, kNeedDrain } = require('internal/http');
5555
const { AbortError, connResetException, codes } = require('internal/errors');
5656
const {
@@ -96,7 +96,7 @@ function ClientRequest(input, options, cb) {
9696
if (typeof input === 'string') {
9797
const urlStr = input;
9898
try {
99-
input = urlToOptions(new URL(urlStr));
99+
input = urlToHttpOptions(new URL(urlStr));
100100
} catch (err) {
101101
input = url.parse(urlStr);
102102
if (!input.hostname) {
@@ -113,7 +113,7 @@ function ClientRequest(input, options, cb) {
113113
} else if (input && input[searchParamsSymbol] &&
114114
input[searchParamsSymbol][searchParamsSymbol]) {
115115
// url.URL instance
116-
input = urlToOptions(input);
116+
input = urlToHttpOptions(input);
117117
} else {
118118
cb = options;
119119
options = input;

lib/https.js

+3-3
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ const { ClientRequest } = require('_http_client');
4848
let debug = require('internal/util/debuglog').debuglog('https', (fn) => {
4949
debug = fn;
5050
});
51-
const { URL, urlToOptions, searchParamsSymbol } = require('internal/url');
51+
const { URL, urlToHttpOptions, searchParamsSymbol } = require('internal/url');
5252
const { IncomingMessage, ServerResponse } = require('http');
5353
const { kIncomingMessage } = require('_http_common');
5454

@@ -344,7 +344,7 @@ function request(...args) {
344344
if (typeof args[0] === 'string') {
345345
const urlStr = ArrayPrototypeShift(args);
346346
try {
347-
options = urlToOptions(new URL(urlStr));
347+
options = urlToHttpOptions(new URL(urlStr));
348348
} catch (err) {
349349
options = url.parse(urlStr);
350350
if (!options.hostname) {
@@ -361,7 +361,7 @@ function request(...args) {
361361
} else if (args[0] && args[0][searchParamsSymbol] &&
362362
args[0][searchParamsSymbol][searchParamsSymbol]) {
363363
// url.URL instance
364-
options = urlToOptions(ArrayPrototypeShift(args));
364+
options = urlToHttpOptions(ArrayPrototypeShift(args));
365365
}
366366

367367
if (args[0] && typeof args[0] !== 'function') {

lib/internal/url.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -1295,7 +1295,7 @@ function domainToUnicode(domain) {
12951295
// Utility function that converts a URL object into an ordinary
12961296
// options object as expected by the http.request and https.request
12971297
// APIs.
1298-
function urlToOptions(url) {
1298+
function urlToHttpOptions(url) {
12991299
const options = {
13001300
protocol: url.protocol,
13011301
hostname: typeof url.hostname === 'string' &&
@@ -1494,7 +1494,7 @@ module.exports = {
14941494
URLSearchParams,
14951495
domainToASCII,
14961496
domainToUnicode,
1497-
urlToOptions,
1497+
urlToHttpOptions,
14981498
formatSymbol: kFormat,
14991499
searchParamsSymbol: searchParams,
15001500
encodeStr

lib/url.js

+4-2
Original file line numberDiff line numberDiff line change
@@ -48,9 +48,10 @@ const {
4848
URLSearchParams,
4949
domainToASCII,
5050
domainToUnicode,
51+
fileURLToPath,
5152
formatSymbol,
5253
pathToFileURL,
53-
fileURLToPath
54+
urlToHttpOptions,
5455
} = require('internal/url');
5556

5657
// Original url.parse() API
@@ -988,5 +989,6 @@ module.exports = {
988989

989990
// Utilities
990991
pathToFileURL,
991-
fileURLToPath
992+
fileURLToPath,
993+
urlToHttpOptions,
992994
};
+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
'use strict';
2+
require('../common');
3+
const assert = require('assert');
4+
const { urlToHttpOptions } = require('url');
5+
6+
// Test urlToHttpOptions
7+
const urlObj = new URL('http://user:[email protected]:21/aaa/zzz?l=24#test');
8+
const opts = urlToHttpOptions(urlObj);
9+
assert.strictEqual(opts instanceof URL, false);
10+
assert.strictEqual(opts.protocol, 'http:');
11+
assert.strictEqual(opts.auth, 'user:pass');
12+
assert.strictEqual(opts.hostname, 'foo.bar.com');
13+
assert.strictEqual(opts.port, 21);
14+
assert.strictEqual(opts.path, '/aaa/zzz?l=24');
15+
assert.strictEqual(opts.pathname, '/aaa/zzz');
16+
assert.strictEqual(opts.search, '?l=24');
17+
assert.strictEqual(opts.hash, '#test');
18+
19+
const { hostname } = urlToHttpOptions(new URL('http://[::1]:21'));
20+
assert.strictEqual(hostname, '::1');
21+
22+
// If a WHATWG URL object is copied, it is possible that the resulting copy
23+
// contains the Symbols that Node uses for brand checking, but not the data
24+
// properties, which are getters. Verify that urlToHttpOptions() can handle
25+
// such a case.
26+
const copiedUrlObj = { ...urlObj };
27+
const copiedOpts = urlToHttpOptions(copiedUrlObj);
28+
assert.strictEqual(copiedOpts instanceof URL, false);
29+
assert.strictEqual(copiedOpts.protocol, undefined);
30+
assert.strictEqual(copiedOpts.auth, undefined);
31+
assert.strictEqual(copiedOpts.hostname, undefined);
32+
assert.strictEqual(copiedOpts.port, NaN);
33+
assert.strictEqual(copiedOpts.path, '');
34+
assert.strictEqual(copiedOpts.pathname, undefined);
35+
assert.strictEqual(copiedOpts.search, undefined);
36+
assert.strictEqual(copiedOpts.hash, undefined);
37+
assert.strictEqual(copiedOpts.href, undefined);

test/parallel/test-whatwg-url-custom-properties.js

-36
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55

66
require('../common');
77
const assert = require('assert');
8-
const urlToOptions = require('internal/url').urlToOptions;
98

109
const url = new URL('http://user:[email protected]:21/aaa/zzz?l=24#test');
1110
const oldParams = url.searchParams; // For test of [SameObject]
@@ -130,41 +129,6 @@ assert.strictEqual(url.toString(),
130129
assert.strictEqual((delete url.searchParams), true);
131130
assert.strictEqual(url.searchParams, oldParams);
132131

133-
// Test urlToOptions
134-
{
135-
const urlObj = new URL('http://user:[email protected]:21/aaa/zzz?l=24#test');
136-
const opts = urlToOptions(urlObj);
137-
assert.strictEqual(opts instanceof URL, false);
138-
assert.strictEqual(opts.protocol, 'http:');
139-
assert.strictEqual(opts.auth, 'user:pass');
140-
assert.strictEqual(opts.hostname, 'foo.bar.com');
141-
assert.strictEqual(opts.port, 21);
142-
assert.strictEqual(opts.path, '/aaa/zzz?l=24');
143-
assert.strictEqual(opts.pathname, '/aaa/zzz');
144-
assert.strictEqual(opts.search, '?l=24');
145-
assert.strictEqual(opts.hash, '#test');
146-
147-
const { hostname } = urlToOptions(new URL('http://[::1]:21'));
148-
assert.strictEqual(hostname, '::1');
149-
150-
// If a WHATWG URL object is copied, it is possible that the resulting copy
151-
// contains the Symbols that Node uses for brand checking, but not the data
152-
// properties, which are getters. Verify that urlToOptions() can handle such
153-
// a case.
154-
const copiedUrlObj = { ...urlObj };
155-
const copiedOpts = urlToOptions(copiedUrlObj);
156-
assert.strictEqual(copiedOpts instanceof URL, false);
157-
assert.strictEqual(copiedOpts.protocol, undefined);
158-
assert.strictEqual(copiedOpts.auth, undefined);
159-
assert.strictEqual(copiedOpts.hostname, undefined);
160-
assert.strictEqual(copiedOpts.port, NaN);
161-
assert.strictEqual(copiedOpts.path, '');
162-
assert.strictEqual(copiedOpts.pathname, undefined);
163-
assert.strictEqual(copiedOpts.search, undefined);
164-
assert.strictEqual(copiedOpts.hash, undefined);
165-
assert.strictEqual(copiedOpts.href, undefined);
166-
}
167-
168132
// Test special origins
169133
[
170134
{ expected: 'https://whatwg.org',

0 commit comments

Comments
 (0)