Skip to content

Commit 64fed31

Browse files
zhangyongshengruyadorno
zhangyongsheng
authored andcommitted
url: expose urlToHttpOptions utility
PR-URL: #35960 Fixes: #34349 Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Michaël Zasso <targos@protonmail.com>
1 parent 3ea37c2 commit 64fed31

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
@@ -1029,6 +1029,50 @@ new URL('/some/path%.c', 'file:'); // Incorrect: file:///some/path%.c
10291029
pathToFileURL('/some/path%.c'); // Correct: file:///some/path%25.c (POSIX)
10301030
```
10311031

1032+
### `url.urlToHttpOptions(url)`
1033+
<!-- YAML
1034+
added: REPLACEME
1035+
-->
1036+
1037+
* `url` {URL} The [WHATWG URL][] object to convert to an options object.
1038+
* Returns: {Object} Options object
1039+
* `protocol` {string} Protocol to use.
1040+
* `hostname` {string} A domain name or IP address of the server to issue the
1041+
request to.
1042+
* `hash` {string} The fragment portion of the URL.
1043+
* `search` {string} The serialized query portion of the URL.
1044+
* `pathname` {string} The path portion of the URL.
1045+
* `path` {string} Request path. Should include query string if any.
1046+
E.G. `'/index.html?page=12'`. An exception is thrown when the request path
1047+
contains illegal characters. Currently, only spaces are rejected but that
1048+
may change in the future.
1049+
* `href` {string} The serialized URL.
1050+
* `port` {number} Port of remote server.
1051+
* `auth` {string} Basic authentication i.e. `'user:password'` to compute an
1052+
Authorization header.
1053+
1054+
This utility function converts a URL object into an ordinary options object as
1055+
expected by the [`http.request()`][] and [`https.request()`][] APIs.
1056+
1057+
```js
1058+
const { urlToHttpOptions } = require('url');
1059+
const myURL = new URL('https://a:b@測試?abc#foo');
1060+
1061+
console.log(urlToHttpOptions(myUrl));
1062+
/**
1063+
{
1064+
protocol: 'https:',
1065+
hostname: 'xn--g6w251d',
1066+
hash: '#foo',
1067+
search: '?abc',
1068+
pathname: '/',
1069+
path: '/?abc',
1070+
href: 'https://a:b@xn--g6w251d/?abc#foo',
1071+
auth: 'a:b'
1072+
}
1073+
*/
1074+
```
1075+
10321076
## Legacy URL API
10331077
<!-- YAML
10341078
deprecated: v11.0.0
@@ -1388,6 +1432,8 @@ console.log(myURL.origin);
13881432
[`TypeError`]: errors.md#errors_class_typeerror
13891433
[`URLSearchParams`]: #url_class_urlsearchparams
13901434
[`array.toString()`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/toString
1435+
[`http.request()`]: http.md#http_http_request_options_callback
1436+
[`https.request()`]: https.md#https_https_request_options_callback
13911437
[`new URL()`]: #url_new_url_input_base
13921438
[`querystring`]: querystring.md
13931439
[`require('url').format()`]: #url_url_format_url_options

lib/_http_client.js

+3-3
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ const { OutgoingMessage } = require('_http_outgoing');
5858
const Agent = require('_http_agent');
5959
const { Buffer } = require('buffer');
6060
const { defaultTriggerAsyncIdScope } = require('internal/async_hooks');
61-
const { URL, urlToOptions, searchParamsSymbol } = require('internal/url');
61+
const { URL, urlToHttpOptions, searchParamsSymbol } = require('internal/url');
6262
const { kOutHeaders, kNeedDrain } = require('internal/http');
6363
const { connResetException, codes } = require('internal/errors');
6464
const {
@@ -105,7 +105,7 @@ function ClientRequest(input, options, cb) {
105105
if (typeof input === 'string') {
106106
const urlStr = input;
107107
try {
108-
input = urlToOptions(new URL(urlStr));
108+
input = urlToHttpOptions(new URL(urlStr));
109109
} catch (err) {
110110
input = url.parse(urlStr);
111111
if (!input.hostname) {
@@ -122,7 +122,7 @@ function ClientRequest(input, options, cb) {
122122
} else if (input && input[searchParamsSymbol] &&
123123
input[searchParamsSymbol][searchParamsSymbol]) {
124124
// url.URL instance
125-
input = urlToOptions(input);
125+
input = urlToHttpOptions(input);
126126
} else {
127127
cb = options;
128128
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

@@ -303,7 +303,7 @@ function request(...args) {
303303
if (typeof args[0] === 'string') {
304304
const urlStr = ArrayPrototypeShift(args);
305305
try {
306-
options = urlToOptions(new URL(urlStr));
306+
options = urlToHttpOptions(new URL(urlStr));
307307
} catch (err) {
308308
options = url.parse(urlStr);
309309
if (!options.hostname) {
@@ -320,7 +320,7 @@ function request(...args) {
320320
} else if (args[0] && args[0][searchParamsSymbol] &&
321321
args[0][searchParamsSymbol][searchParamsSymbol]) {
322322
// url.URL instance
323-
options = urlToOptions(ArrayPrototypeShift(args));
323+
options = urlToHttpOptions(ArrayPrototypeShift(args));
324324
}
325325

326326
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
@@ -47,9 +47,10 @@ const {
4747
URLSearchParams,
4848
domainToASCII,
4949
domainToUnicode,
50+
fileURLToPath,
5051
formatSymbol,
5152
pathToFileURL,
52-
fileURLToPath
53+
urlToHttpOptions,
5354
} = require('internal/url');
5455

5556
// Original url.parse() API
@@ -987,5 +988,6 @@ module.exports = {
987988

988989
// Utilities
989990
pathToFileURL,
990-
fileURLToPath
991+
fileURLToPath,
992+
urlToHttpOptions,
991993
};
+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:pass@foo.bar.com: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
@@ -6,7 +6,6 @@
66
require('../common');
77
const URL = require('url').URL;
88
const assert = require('assert');
9-
const urlToOptions = require('internal/url').urlToOptions;
109

1110
const url = new URL('http://user:pass@foo.bar.com:21/aaa/zzz?l=24#test');
1211
const oldParams = url.searchParams; // For test of [SameObject]
@@ -131,41 +130,6 @@ assert.strictEqual(url.toString(),
131130
assert.strictEqual((delete url.searchParams), true);
132131
assert.strictEqual(url.searchParams, oldParams);
133132

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

0 commit comments

Comments
 (0)