diff --git a/lib/_http_client.js b/lib/_http_client.js index 6a070b0f0a10..cad31bc676fa 100644 --- a/lib/_http_client.js +++ b/lib/_http_client.js @@ -29,6 +29,7 @@ const { ObjectAssign, ObjectDefineProperty, ObjectKeys, + ObjectPrototypeHasOwnProperty, ObjectSetPrototypeOf, ReflectApply, String, @@ -331,12 +332,15 @@ function rewriteForProxiedHttp(req, reqOptions, proxyAuthority, userHostHeader, function ClientRequest(input, options, cb) { OutgoingMessage.call(this); + let pathIsFromURL = false; if (typeof input === 'string') { const urlStr = input; input = urlToHttpOptions(new URL(urlStr)); + pathIsFromURL = true; } else if (isURL(input)) { // url.URL instance input = urlToHttpOptions(input); + pathIsFromURL = true; } else { cb = options; options = input; @@ -347,6 +351,13 @@ function ClientRequest(input, options, cb) { cb = options; options = input || kEmptyObject; } else { + const hasPathOverride = pathIsFromURL && + options != null && + ObjectPrototypeHasOwnProperty(options, 'path'); + if (hasPathOverride) { + pathIsFromURL = false; + } + options = ObjectAssign({ __proto__: null }, input, options); } @@ -466,7 +477,13 @@ function ClientRequest(input, options, cb) { this.joinDuplicateHeaders = options.joinDuplicateHeaders; - this[kPath] = options.path || '/'; + let path = options.path || '/'; + // Strip the leading slash added when the CONNECT target comes from a URL. + if (method === 'CONNECT' && pathIsFromURL && path[0] === '/') { + path = path.slice(1) || '/'; + } + + this[kPath] = path; if (cb) { this.once('response', cb); } diff --git a/test/parallel/test-http-request-connect-path.js b/test/parallel/test-http-request-connect-path.js new file mode 100644 index 000000000000..91b3cc739431 --- /dev/null +++ b/test/parallel/test-http-request-connect-path.js @@ -0,0 +1,56 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const http = require('http'); + +{ + const server = http.createServer(common.mustNotCall()); + + server.on('connect', common.mustCall((req, socket) => { + assert.strictEqual(req.url, 'example.com'); + socket.end('HTTP/1.1 501 Not Implemented\r\n\r\n'); + })); + + server.listen(0, common.mustCall(() => { + const port = server.address().port; + const req = http.request( + new URL(`http://localhost:${port}/example.com`), + { method: 'CONNECT' }, + ); + + req.on('connect', common.mustCall((res, socket) => { + assert.strictEqual(res.statusCode, 501); + socket.destroy(); + server.close(); + })); + + req.end(); + })); +} + +{ + const server = http.createServer(common.mustNotCall()); + + server.on('connect', common.mustCall((req, socket) => { + assert.strictEqual(req.url, '/example.com'); + socket.end('HTTP/1.1 501 Not Implemented\r\n\r\n'); + })); + + server.listen(0, common.mustCall(() => { + const req = http.request({ + host: 'localhost', + port: server.address().port, + method: 'CONNECT', + path: '/example.com', + }); + + req.on('connect', common.mustCall((res, socket) => { + assert.strictEqual(res.statusCode, 501); + socket.destroy(); + server.close(); + })); + + req.end(); + })); +}