Skip to content

Commit c0681e5

Browse files
authored
http: normalize CONNECT request paths
Signed-off-by: Efe Karasakal <hi@efe.dev> PR-URL: #64876 Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com> Reviewed-By: Tim Perry <pimterry@gmail.com>
1 parent 95279e7 commit c0681e5

2 files changed

Lines changed: 74 additions & 1 deletion

File tree

‎lib/_http_client.js‎

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ const {
2828
NumberIsFinite,
2929
ObjectAssign,
3030
ObjectDefineProperty,
31+
ObjectHasOwn,
3132
ObjectKeys,
3233
ObjectSetPrototypeOf,
3334
ReflectApply,
@@ -336,12 +337,15 @@ function ClientRequest(input, options, cb) {
336337

337338
OutgoingMessage.call(this);
338339

340+
let pathIsFromURL = false;
339341
if (typeof input === 'string') {
340342
const urlStr = input;
341343
input = urlToHttpOptions(new URL(urlStr));
344+
pathIsFromURL = true;
342345
} else if (isURL(input)) {
343346
// url.URL instance
344347
input = urlToHttpOptions(input);
348+
pathIsFromURL = true;
345349
} else {
346350
cb = options;
347351
options = input;
@@ -352,6 +356,13 @@ function ClientRequest(input, options, cb) {
352356
cb = options;
353357
options = input || kEmptyObject;
354358
} else {
359+
const hasPathOverride = pathIsFromURL &&
360+
options != null &&
361+
ObjectHasOwn(options, 'path');
362+
if (hasPathOverride) {
363+
pathIsFromURL = false;
364+
}
365+
355366
options = ObjectAssign({ __proto__: null }, input, options);
356367
}
357368

@@ -471,7 +482,13 @@ function ClientRequest(input, options, cb) {
471482

472483
this.joinDuplicateHeaders = options.joinDuplicateHeaders;
473484

474-
this[kPath] = options.path || '/';
485+
let path = options.path || '/';
486+
// Strip the leading slash added when the CONNECT target comes from a URL.
487+
if (method === 'CONNECT' && pathIsFromURL && path[0] === '/') {
488+
path = path.slice(1) || '/';
489+
}
490+
491+
this[kPath] = path;
475492
if (cb) {
476493
this.once('response', cb);
477494
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
'use strict';
2+
3+
const common = require('../common');
4+
const assert = require('assert');
5+
const http = require('http');
6+
7+
{
8+
const server = http.createServer(common.mustNotCall());
9+
10+
server.on('connect', common.mustCall((req, socket) => {
11+
assert.strictEqual(req.url, 'example.com');
12+
socket.end('HTTP/1.1 501 Not Implemented\r\n\r\n');
13+
}));
14+
15+
server.listen(0, common.mustCall(() => {
16+
const port = server.address().port;
17+
const req = http.request(
18+
new URL(`http://localhost:${port}/example.com`),
19+
{ method: 'CONNECT' },
20+
);
21+
22+
req.on('connect', common.mustCall((res, socket) => {
23+
assert.strictEqual(res.statusCode, 501);
24+
socket.destroy();
25+
server.close();
26+
}));
27+
28+
req.end();
29+
}));
30+
}
31+
32+
{
33+
const server = http.createServer(common.mustNotCall());
34+
35+
server.on('connect', common.mustCall((req, socket) => {
36+
assert.strictEqual(req.url, '/example.com');
37+
socket.end('HTTP/1.1 501 Not Implemented\r\n\r\n');
38+
}));
39+
40+
server.listen(0, common.mustCall(() => {
41+
const req = http.request({
42+
host: 'localhost',
43+
port: server.address().port,
44+
method: 'CONNECT',
45+
path: '/example.com',
46+
});
47+
48+
req.on('connect', common.mustCall((res, socket) => {
49+
assert.strictEqual(res.statusCode, 501);
50+
socket.destroy();
51+
server.close();
52+
}));
53+
54+
req.end();
55+
}));
56+
}

0 commit comments

Comments
 (0)