Skip to content

Commit e88a573

Browse files
committed
http: runtime deprecate instantiating without new
Promote DEP0195 from documentation-only to a runtime deprecation. Calling node:http constructors without `new` now emits DEP0195 via deprecateInstantiation. This covers Agent, Server, OutgoingMessage, IncomingMessage, ServerResponse, and ClientRequest. Update in-tree tests that called Server/Agent without `new` to use the keyword, and add a dedicated DEP0195 coverage test. Refs: #58518 Assisted-by: Grok Signed-off-by: Yagiz Nizipli <yagiz@nizipli.com>
1 parent eea8d72 commit e88a573

41 files changed

Lines changed: 142 additions & 43 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

doc/api/deprecations.md

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4281,18 +4281,21 @@ The support for priority signaling has been removed following its deprecation in
42814281

42824282
<!-- YAML
42834283
changes:
4284+
- version: REPLACEME
4285+
pr-url: https://github.com/nodejs/node/pull/0
4286+
description: Runtime deprecation.
42844287
- version:
42854288
- v24.2.0
42864289
- v22.17.0
42874290
pr-url: https://github.com/nodejs/node/pull/58518
42884291
description: Documentation-only deprecation.
42894292
-->
42904293

4291-
Type: Documentation-only
4294+
Type: Runtime
42924295

42934296
Instantiating classes without the `new` qualifier exported by the `node:http` module is deprecated.
42944297
It is recommended to use the `new` qualifier instead. This applies to all http classes, such as
4295-
`OutgoingMessage`, `IncomingMessage`, `ServerResponse` and `ClientRequest`.
4298+
`OutgoingMessage`, `IncomingMessage`, `ServerResponse`, `ClientRequest`, `Server`, and `Agent`.
42964299

42974300
An automated migration is available ([source](https://github.com/nodejs/userland-migrations/tree/main/recipes/http-classes-with-new)):
42984301

lib/_http_agent.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ const {
4545
const { AsyncResource } = require('async_hooks');
4646
const { async_id_symbol } = require('internal/async_hooks').symbols;
4747
const {
48+
deprecateInstantiation,
4849
getLazy,
4950
kEmptyObject,
5051
once,
@@ -88,8 +89,9 @@ function freeSocketErrorListener(err) {
8889
}
8990

9091
function Agent(options) {
91-
if (!(this instanceof Agent))
92-
return new Agent(options);
92+
if (!(this instanceof Agent)) {
93+
return deprecateInstantiation(Agent, 'DEP0195', options);
94+
}
9395

9496
EventEmitter.call(this);
9597

lib/_http_client.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ const {
3838
const net = require('net');
3939
const assert = require('internal/assert');
4040
const {
41+
deprecateInstantiation,
4142
kEmptyObject,
4243
once,
4344
} = require('internal/util');
@@ -190,6 +191,10 @@ function rewriteForProxiedHttp(req, reqOptions) {
190191
};
191192

192193
function ClientRequest(input, options, cb) {
194+
if (!(this instanceof ClientRequest)) {
195+
return deprecateInstantiation(ClientRequest, 'DEP0195', input, options, cb);
196+
}
197+
193198
OutgoingMessage.call(this);
194199

195200
if (typeof input === 'string') {

lib/_http_incoming.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ const {
2828
} = primordials;
2929

3030
const { Readable, finished } = require('stream');
31+
const {
32+
deprecateInstantiation,
33+
} = require('internal/util');
3134

3235
const { AbortController } = require('internal/abort_controller');
3336

@@ -51,6 +54,10 @@ function readStop(socket) {
5154

5255
/* Abstract base class for ServerRequest and ClientResponse. */
5356
function IncomingMessage(socket) {
57+
if (!(this instanceof IncomingMessage)) {
58+
return deprecateInstantiation(IncomingMessage, 'DEP0195', socket);
59+
}
60+
5461
let streamOptions;
5562

5663
if (socket) {

lib/_http_outgoing.js

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,10 @@ const {
7171
hideStackFrames,
7272
} = require('internal/errors');
7373
const { validateString } = require('internal/validators');
74-
const { assignFunctionName } = require('internal/util');
74+
const {
75+
assignFunctionName,
76+
deprecateInstantiation,
77+
} = require('internal/util');
7578
const { isUint8Array } = require('internal/util/types');
7679

7780
let debug = require('internal/util/debuglog').debuglog('http', (fn) => {
@@ -104,6 +107,10 @@ function isContentDispositionField(s) {
104107
}
105108

106109
function OutgoingMessage(options) {
110+
if (!(this instanceof OutgoingMessage)) {
111+
return deprecateInstantiation(OutgoingMessage, 'DEP0195', options);
112+
}
113+
107114
Stream.call(this);
108115

109116
// Queue that holds all currently pending data, until the response will be

lib/_http_server.js

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ const {
8686
} = require('internal/errors');
8787
const {
8888
assignFunctionName,
89+
deprecateInstantiation,
8990
kEmptyObject,
9091
promisify,
9192
} = require('internal/util');
@@ -202,6 +203,10 @@ class HTTPServerAsyncResource {
202203
}
203204

204205
function ServerResponse(req, options) {
206+
if (!(this instanceof ServerResponse)) {
207+
return deprecateInstantiation(ServerResponse, 'DEP0195', req, options);
208+
}
209+
205210
OutgoingMessage.call(this, options);
206211

207212
if (req.method === 'HEAD') this._hasBody = false;
@@ -627,7 +632,9 @@ function httpServerPreClose(server) {
627632
}
628633

629634
function Server(options, requestListener) {
630-
if (!(this instanceof Server)) return new Server(options, requestListener);
635+
if (!(this instanceof Server)) {
636+
return deprecateInstantiation(Server, 'DEP0195', options, requestListener);
637+
}
631638

632639
if (typeof options === 'function') {
633640
requestListener = options;

test/parallel/test-cluster-primary-error.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ const totalWorkers = 2;
2929
// Cluster setup
3030
if (cluster.isWorker) {
3131
const http = require('http');
32-
http.Server(() => {}).listen(0, '127.0.0.1');
32+
new http.Server(() => {}).listen(0, '127.0.0.1');
3333
} else if (process.argv[2] === 'cluster') {
3434
// Send PID to testcase process
3535
let forkNum = 0;

test/parallel/test-cluster-primary-kill.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ if (cluster.isWorker) {
2828

2929
// Keep the worker alive
3030
const http = require('http');
31-
http.Server().listen(0, '127.0.0.1');
31+
new http.Server().listen(0, '127.0.0.1');
3232

3333
} else if (process.argv[2] === 'cluster') {
3434

test/parallel/test-cluster-rr-domain-listen.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ if (cluster.isWorker) {
3232
d.run(() => {});
3333

3434
const http = require('http');
35-
http.Server(() => {}).listen(0, '127.0.0.1');
35+
new http.Server(() => {}).listen(0, '127.0.0.1');
3636

3737
} else if (cluster.isPrimary) {
3838

test/parallel/test-cluster-worker-disconnect.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ const cluster = require('cluster');
2626

2727
if (cluster.isWorker) {
2828
const http = require('http');
29-
http.Server(() => {
29+
new http.Server(() => {
3030

3131
}).listen(0, '127.0.0.1');
3232

0 commit comments

Comments
 (0)