diff --git a/doc/api/http.md b/doc/api/http.md index 14b900ac55f035..8336bde8f1d27b 100644 --- a/doc/api/http.md +++ b/doc/api/http.md @@ -2155,6 +2155,10 @@ This can be overridden for servers and client requests by passing the * {string} @@ -488,7 +495,8 @@ console.log(myURL.href); Any invalid URL characters appearing in the value assigned the `username` property will be [percent-encoded][]. The selection of which characters to percent-encode may vary somewhat from what the [`url.parse()`][] -and [`url.format()`][] methods would produce. +and [`url.format()`][] methods would produce. When used with +[`http.request()`][], this field will be percent-decoded. #### `url.toString()` @@ -1316,6 +1324,7 @@ console.log(myURL.origin); [`TypeError`]: errors.html#errors_class_typeerror [`URLSearchParams`]: #url_class_urlsearchparams [`array.toString()`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/toString +[`http.request()`]: http.html#http_http_request_options_callback [`new URL()`]: #url_constructor_new_url_input_base [`querystring`]: querystring.html [`require('url').format()`]: #url_url_format_url_options diff --git a/lib/internal/url.js b/lib/internal/url.js index 09fa9f2cf47d50..3bd9de1a883bb6 100644 --- a/lib/internal/url.js +++ b/lib/internal/url.js @@ -1265,6 +1265,10 @@ function domainToUnicode(domain) { return _domainToUnicode(`${domain}`); } +function decodeAuth(str) { + return decodeURIComponent(str).replace(':', '%3A').replace('/', '%2F'); +} + // Utility function that converts a URL object into an ordinary // options object as expected by the http.request and https.request // APIs. @@ -1284,7 +1288,7 @@ function urlToOptions(url) { options.port = Number(url.port); } if (url.username || url.password) { - options.auth = `${url.username}:${url.password}`; + options.auth = `${decodeAuth(url.username)}:${decodeAuth(url.password)}`; } return options; } diff --git a/test/parallel/test-http-url-username.js b/test/parallel/test-http-url-username.js new file mode 100644 index 00000000000000..85956c916bd1a6 --- /dev/null +++ b/test/parallel/test-http-url-username.js @@ -0,0 +1,33 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const http = require('http'); +const MakeDuplexPair = require('../common/duplexpair'); + +// Test that usernames from URLs are URL-decoded, as they should be. + +{ + const url = new URL('http://localhost'); + url.username = 'test@test"'; + url.password = '123456^'; + + const server = http.createServer( + common.mustCall((req, res) => { + assert.strictEqual( + req.headers.authorization, + 'Basic ' + Buffer.from('test@test":123456^').toString('base64')); + res.statusCode = 200; + res.end(); + })); + + const { clientSide, serverSide } = MakeDuplexPair(); + server.emit('connection', serverSide); + + const req = http.request(url, { + createConnection: common.mustCall(() => clientSide) + }, common.mustCall((res) => { + res.resume(); // We don’t actually care about contents. + res.on('end', common.mustCall()); + })); + req.end(); +}