Skip to content

Commit 25c0c8e

Browse files
authored
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> PR-URL: #64853 Reviewed-By: Aviv Keller <me@aviv.sh> Reviewed-By: Tim Perry <pimterry@gmail.com> Reviewed-By: Gürgün Dayıoğlu <hey@gurgun.day> Reviewed-By: Filip Skokan <panva.ip@gmail.com> Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
1 parent 276fe2b commit 25c0c8e

42 files changed

Lines changed: 144 additions & 44 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
@@ -4341,18 +4341,21 @@ npx codemod@latest @nodejs/http2-priority-signaling
43414341

43424342
<!-- YAML
43434343
changes:
4344+
- version: REPLACEME
4345+
pr-url: https://github.com/nodejs/node/pull/64853
4346+
description: Runtime deprecation.
43444347
- version:
43454348
- v24.2.0
43464349
- v22.17.0
43474350
pr-url: https://github.com/nodejs/node/pull/58518
43484351
description: Documentation-only deprecation.
43494352
-->
43504353

4351-
Type: Documentation-only
4354+
Type: Runtime
43524355

43534356
Instantiating classes without the `new` qualifier exported by the `node:http` module is deprecated.
43544357
It is recommended to use the `new` qualifier instead. This applies to all http classes, such as
4355-
`OutgoingMessage`, `IncomingMessage`, `ServerResponse` and `ClientRequest`.
4358+
`OutgoingMessage`, `IncomingMessage`, `ServerResponse`, `ClientRequest`, `Server`, and `Agent`.
43564359

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

lib/_http_agent.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ const {
4848
owner_symbol,
4949
} = require('internal/async_hooks').symbols;
5050
const {
51+
deprecateInstantiation,
5152
getLazy,
5253
kEmptyObject,
5354
once,
@@ -151,8 +152,9 @@ function removeFreeSocketDataGuard(socket) {
151152
}
152153

153154
function Agent(options) {
154-
if (!(this instanceof Agent))
155-
return new Agent(options);
155+
if (!(this instanceof Agent)) {
156+
return deprecateInstantiation(Agent, 'DEP0195', options);
157+
}
156158

157159
EventEmitter.call(this);
158160

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');
@@ -329,6 +330,10 @@ function rewriteForProxiedHttp(req, reqOptions, proxyAuthority, userHostHeader,
329330
};
330331

331332
function ClientRequest(input, options, cb) {
333+
if (!(this instanceof ClientRequest)) {
334+
return deprecateInstantiation(ClientRequest, 'DEP0195', input, options, cb);
335+
}
336+
332337
OutgoingMessage.call(this);
333338

334339
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

@@ -56,6 +59,10 @@ function readStop(socket) {
5659

5760
/* Abstract base class for ServerRequest and ClientResponse. */
5861
function IncomingMessage(socket) {
62+
if (!(this instanceof IncomingMessage)) {
63+
return deprecateInstantiation(IncomingMessage, 'DEP0195', socket);
64+
}
65+
5966
let streamOptions;
6067

6168
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
@@ -89,6 +89,7 @@ const {
8989
} = require('internal/errors');
9090
const {
9191
assignFunctionName,
92+
deprecateInstantiation,
9293
kEmptyObject,
9394
promisify,
9495
} = require('internal/util');
@@ -205,6 +206,10 @@ class HTTPServerAsyncResource {
205206
}
206207

207208
function ServerResponse(req, options) {
209+
if (!(this instanceof ServerResponse)) {
210+
return deprecateInstantiation(ServerResponse, 'DEP0195', req, options);
211+
}
212+
208213
OutgoingMessage.call(this, options);
209214

210215
if (req.method === 'HEAD') this._hasBody = false;
@@ -630,7 +635,9 @@ function httpServerPreClose(server) {
630635
}
631636

632637
function Server(options, requestListener) {
633-
if (!(this instanceof Server)) return new Server(options, requestListener);
638+
if (!(this instanceof Server)) {
639+
return deprecateInstantiation(Server, 'DEP0195', options, requestListener);
640+
}
634641

635642
if (typeof options === 'function') {
636643
requestListener = options;

test/parallel/test-cluster-basic.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,8 @@ function forEach(obj, fn) {
5050

5151

5252
if (cluster.isWorker) {
53-
require('http').Server(common.mustNotCall()).listen(0, '127.0.0.1');
53+
const http = require('http');
54+
new http.Server(common.mustNotCall()).listen(0, '127.0.0.1');
5455
} else if (cluster.isPrimary) {
5556

5657
const checks = {

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

0 commit comments

Comments
 (0)