Skip to content

Commit 75c10f8

Browse files
authored
Merge branch 'nodejs:main' into quic/document-stream-stopSending-resetStream
2 parents 3cacf44 + 39b481b commit 75c10f8

50 files changed

Lines changed: 1358 additions & 243 deletions

Some content is hidden

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

CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,8 @@ release.
4242
</tr>
4343
<tr>
4444
<td valign="top">
45-
<b><a href="doc/changelogs/CHANGELOG_V26.md#26.2.0">26.2.0</a></b><br/>
45+
<b><a href="doc/changelogs/CHANGELOG_V26.md#26.3.0">26.3.0</a></b><br/>
46+
<a href="doc/changelogs/CHANGELOG_V26.md#26.2.0">26.2.0</a><br/>
4647
<a href="doc/changelogs/CHANGELOG_V26.md#26.1.0">26.1.0</a><br/>
4748
<a href="doc/changelogs/CHANGELOG_V26.md#26.0.0">26.0.0</a><br/>
4849
</td>
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
'use strict';
2+
3+
const common = require('../common.js');
4+
5+
const bench = common.createBenchmark(main, {
6+
size: [64, 1024, 16384, 262144, 4194304],
7+
content: ['ascii', 'latin1', 'utf8_mixed', 'latin1_then_cjk'],
8+
n: [1e4],
9+
});
10+
11+
function buildBuffer(kind, size) {
12+
if (kind === 'ascii') {
13+
return Buffer.alloc(size, 0x61);
14+
}
15+
if (kind === 'latin1') {
16+
const pair = Buffer.from([0xC3, 0xA9]);
17+
const buf = Buffer.alloc(size);
18+
for (let i = 0; i + 2 <= size; i += 2) pair.copy(buf, i);
19+
return buf;
20+
}
21+
if (kind === 'utf8_mixed') {
22+
const cjk = Buffer.from([0xE4, 0xB8, 0xAD]);
23+
const buf = Buffer.alloc(size);
24+
let i = 0;
25+
while (i + 4 <= size) {
26+
buf[i++] = 0x61;
27+
cjk.copy(buf, i);
28+
i += 3;
29+
}
30+
return buf;
31+
}
32+
if (kind === 'latin1_then_cjk') {
33+
const pair = Buffer.from([0xC3, 0xA9]);
34+
const cjk = Buffer.from([0xE4, 0xB8, 0xAD]);
35+
const buf = Buffer.alloc(size);
36+
const mid = (size >> 1) & ~1;
37+
for (let i = 0; i + 2 <= mid; i += 2) pair.copy(buf, i);
38+
cjk.copy(buf, mid);
39+
for (let i = mid + 3; i + 2 <= size; i += 2) pair.copy(buf, i);
40+
return buf;
41+
}
42+
throw new Error('unknown content: ' + kind);
43+
}
44+
45+
function main({ n, size, content }) {
46+
const buf = buildBuffer(content, size);
47+
48+
bench.start();
49+
for (let i = 0; i < n; i++) {
50+
buf.toString('utf8');
51+
}
52+
bench.end(n);
53+
}

configure.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@
5555
valid_mips_float_abi = ('soft', 'hard')
5656
valid_intl_modes = ('none', 'small-icu', 'full-icu', 'system-icu')
5757
icu_versions = json.loads((tools_path / 'icu' / 'icu_versions.json').read_text(encoding='utf-8'))
58-
maglev_enabled_architectures = ('x64', 'arm', 'arm64', 's390x')
58+
maglev_enabled_architectures = ('x64', 'arm', 'arm64', 'ppc64', 's390x')
5959

6060
# builtins may be removed later if they have been disabled by options
6161
shareable_builtins = {'undici/undici': 'deps/undici/undici.js',
@@ -2175,7 +2175,7 @@ def configure_v8(o, configs):
21752175
o['variables']['v8_promise_internal_field_count'] = 1 # Add internal field to promises for async hooks.
21762176
o['variables']['v8_use_siphash'] = 0 if options.without_siphash else 1
21772177
o['variables']['v8_enable_maglev'] = B(not options.v8_disable_maglev and
2178-
flavor != 'zos' and
2178+
flavor not in ('aix', 'os400', 'zos') and
21792179
o['variables']['target_arch'] in maglev_enabled_architectures)
21802180
o['variables']['v8_enable_pointer_compression'] = 1 if options.enable_pointer_compression else 0
21812181
# Using the sandbox requires always allocating array buffer backing stores in the sandbox.

doc/api/buffer.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1514,7 +1514,7 @@ console.log(Buffer.isEncoding(''));
15141514
<!-- YAML
15151515
added: v0.11.3
15161516
changes:
1517-
- version: REPLACEME
1517+
- version: v26.3.0
15181518
pr-url: https://github.com/nodejs/node/pull/63597
15191519
description: Default raised from 8192 to 65536.
15201520
-->

doc/api/debugger.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -237,7 +237,7 @@ added:
237237
- v26.1.0
238238
- v24.16.0
239239
changes:
240-
- version: REPLACEME
240+
- version: v26.3.0
241241
pr-url: https://github.com/nodejs/node/pull/63437
242242
description: Add `probe_failure` terminal `error` event for inspector-side mid-session
243243
failures, and `error.details` for additional context on per-hit and terminal errors.

doc/api/deprecations.md

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4574,7 +4574,62 @@ throwing an error. This behavior is inconsistent with `hash.digest()` and
45744574
may lead to subtle bugs. Calling `hmac.digest()` on a finalized `Hmac` instance
45754575
will throw an error in a future version.
45764576
4577+
### DEP0207: `.aborted` property and `'aborted'` event in `http2`
4578+
4579+
<!-- YAML
4580+
changes:
4581+
- version: REPLACEME
4582+
pr-url: https://github.com/nodejs/node/pull/63249
4583+
description: Documentation-only deprecation.
4584+
-->
4585+
4586+
Type: Documentation-only
4587+
4588+
Use standard stream events and state checks instead. Read-side aborts
4589+
(peer cancelled before sending `END_STREAM`) now surface as `'error'`
4590+
with code `ERR_HTTP2_STREAM_ABORTED` (clean peer reset code) or
4591+
`ERR_HTTP2_STREAM_ERROR` (non-clean code). Write-side aborts (peer
4592+
cancelled while we still had writes in flight) are detectable from
4593+
`'close'` by checking `writableFinished`. Parallels [DEP0156][] for
4594+
`http`.
4595+
4596+
```cjs
4597+
// Deprecated
4598+
server.on('stream', (stream) => {
4599+
stream.on('aborted', () => {
4600+
// Stream was closed while the writable was still open.
4601+
});
4602+
});
4603+
```
4604+
4605+
```cjs
4606+
// Use this instead
4607+
server.on('stream', (stream) => {
4608+
// Read-side abort: peer cancelled before sending END_STREAM.
4609+
stream.on('error', (err) => {
4610+
if (err.code === 'ERR_HTTP2_STREAM_ABORTED' ||
4611+
err.code === 'ERR_HTTP2_STREAM_ERROR') {
4612+
// Peer cancelled the request mid-stream.
4613+
}
4614+
});
4615+
// Write-side abort: our response didn't fully send before close.
4616+
stream.on('close', () => {
4617+
if (!stream.writableFinished) {
4618+
// Writes were aborted (peer cancel, local destroy, etc.).
4619+
}
4620+
});
4621+
});
4622+
```
4623+
4624+
The same patterns apply to the compatibility API (`req` / `res` on
4625+
`http2.createServer((req, res) => …)`). On the read-side, errors on the
4626+
underlying stream are emitted from `req`. On the write-side you can use
4627+
`res.on('close', …)` to hear about client aborts by checking
4628+
`res.writableFinished` to confirm whether the response was written
4629+
successfully before the response closed.
4630+
45774631
[DEP0142]: #dep0142-repl_builtinlibs
4632+
[DEP0156]: #dep0156-aborted-property-and-abort-aborted-event-in-http
45784633
[NIST SP 800-38D]: https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38d.pdf
45794634
[RFC 6066]: https://tools.ietf.org/html/rfc6066#section-3
45804635
[RFC 8247 Section 2.4]: https://www.rfc-editor.org/rfc/rfc8247#section-2.4

doc/api/errors.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1718,6 +1718,15 @@ Use of the `101` Informational status code is forbidden in HTTP/2.
17181718
An invalid HTTP status code has been specified. Status codes must be an integer
17191719
between `100` and `599` (inclusive).
17201720

1721+
<a id="ERR_HTTP2_STREAM_ABORTED"></a>
1722+
1723+
### `ERR_HTTP2_STREAM_ABORTED`
1724+
1725+
The peer reset the `Http2Stream` with a clean error code (`NGHTTP2_NO_ERROR`
1726+
or `NGHTTP2_CANCEL`) before sending `END_STREAM`, so the readable side will
1727+
not be fully delivered. Mirrors HTTP/1's `ECONNRESET` for a peer-side
1728+
`socket.destroy()`.
1729+
17211730
<a id="ERR_HTTP2_STREAM_CANCEL"></a>
17221731

17231732
### `ERR_HTTP2_STREAM_CANCEL`

doc/api/http.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3680,7 +3680,7 @@ Found'`.
36803680
<!-- YAML
36813681
added: v0.1.13
36823682
changes:
3683-
- version: REPLACEME
3683+
- version: v26.3.0
36843684
pr-url: https://github.com/nodejs/node/pull/61597
36853685
description: The `httpValidation` option is supported now.
36863686
- version:
@@ -4005,7 +4005,7 @@ This can be overridden for servers and client requests by passing the
40054005
<!-- YAML
40064006
added: v0.3.6
40074007
changes:
4008-
- version: REPLACEME
4008+
- version: v26.3.0
40094009
pr-url: https://github.com/nodejs/node/pull/61597
40104010
description: The `httpValidation` option is supported now.
40114011
- version:

doc/api/http2.md

Lines changed: 60 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1238,22 +1238,23 @@ the value is `undefined`, the stream is not yet ready for use.
12381238

12391239
##### Destruction
12401240

1241-
All [`Http2Stream`][] instances are destroyed either when:
1241+
All [`Http2Stream`][] instances are destroyed when one of the following
1242+
happens:
12421243

1243-
* An `RST_STREAM` frame for the stream is received by the connected peer,
1244-
and (for client streams only) pending data has been read.
1245-
* The `http2stream.close()` method is called, and (for client streams only)
1246-
pending data has been read.
1247-
* The `http2stream.destroy()` or `http2session.destroy()` methods are called.
1244+
* Both sides send `END_STREAM` (a clean exchange).
1245+
* The peer sends an `RST_STREAM` frame.
1246+
* `http2stream.close()`, `http2stream.destroy()`, or `http2session.destroy()`
1247+
is called locally.
12481248

1249-
When an `Http2Stream` instance is destroyed, an attempt will be made to send an
1250-
`RST_STREAM` frame to the connected peer.
1249+
For clean exchanges and clean cancels, the destroy is deferred until any
1250+
pending `'end'` and `'finish'` events have fired. When destroyed, an
1251+
attempt is made to send an `RST_STREAM` frame to the connected peer if
1252+
one hasn't already been sent.
12511253

1252-
When the `Http2Stream` instance is destroyed, the `'close'` event will
1253-
be emitted. Because `Http2Stream` is an instance of `stream.Duplex`, the
1254-
`'end'` event will also be emitted if the stream data is currently flowing.
1255-
The `'error'` event may also be emitted if `http2stream.destroy()` was called
1256-
with an `Error` passed as the first argument.
1254+
`'close'` is always emitted on destroy. `'end'` and `'finish'` fire if
1255+
their respective halves completed before destroy. `'error'` fires when
1256+
the destroy carries an error — either via `http2stream.destroy(err)`,
1257+
or when the peer reset the stream before sending `END_STREAM`.
12571258

12581259
After the `Http2Stream` has been destroyed, the `http2stream.destroyed`
12591260
property will be `true` and the `http2stream.rstCode` property will specify the
@@ -1264,14 +1265,18 @@ destroyed.
12641265

12651266
<!-- YAML
12661267
added: v8.4.0
1268+
changes:
1269+
- version: REPLACEME
1270+
pr-url: https://github.com/nodejs/node/pull/63249
1271+
description: Documentation-only deprecation.
12671272
-->
12681273

1269-
The `'aborted'` event is emitted whenever a `Http2Stream` instance is
1270-
abnormally aborted in mid-communication.
1271-
Its listener does not expect any arguments.
1274+
> Stability: 0 - Deprecated. Use `'close'` and `'error'` plus
1275+
> `stream.destroyed`.
12721276
1273-
The `'aborted'` event will only be emitted if the `Http2Stream` writable side
1274-
has not been ended.
1277+
Emitted when an `Http2Stream` is closed before the writable side has
1278+
been ended (via `.end()` or auto-ended via `respond({ endStream: true })`).
1279+
Listeners receive no arguments.
12751280

12761281
#### Event: `'close'`
12771282

@@ -1283,19 +1288,29 @@ The `'close'` event is emitted when the `Http2Stream` is destroyed. Once
12831288
this event is emitted, the `Http2Stream` instance is no longer usable.
12841289

12851290
The HTTP/2 error code used when closing the stream can be retrieved using
1286-
the `http2stream.rstCode` property. If the code is any value other than
1287-
`NGHTTP2_NO_ERROR` (`0`), an `'error'` event will have also been emitted.
1291+
the `http2stream.rstCode` property.
12881292

12891293
#### Event: `'error'`
12901294

12911295
<!-- YAML
12921296
added: v8.4.0
1297+
changes:
1298+
- version: REPLACEME
1299+
pr-url: https://github.com/nodejs/node/pull/63249
1300+
description: >-
1301+
Also emitted on peer-initiated resets that arrive before
1302+
`END_STREAM` (`ERR_HTTP2_STREAM_ABORTED` for clean codes,
1303+
`ERR_HTTP2_STREAM_ERROR` otherwise). Locally-initiated resets
1304+
without an explicit error remain silent.
12931305
-->
12941306

12951307
* `error` {Error}
12961308

1297-
The `'error'` event is emitted when an error occurs during the processing of
1298-
an `Http2Stream`.
1309+
Emitted when an error occurs processing the `Http2Stream`. This includes
1310+
peer-initiated resets that arrive before the readable side has been
1311+
fully delivered: a clean reset code (`NGHTTP2_NO_ERROR` or
1312+
`NGHTTP2_CANCEL`) surfaces as [`ERR_HTTP2_STREAM_ABORTED`][], any other
1313+
code as [`ERR_HTTP2_STREAM_ERROR`][].
12991314

13001315
#### Event: `'frameError'`
13011316

@@ -1378,8 +1393,8 @@ added: v8.4.0
13781393

13791394
* Type: {boolean}
13801395

1381-
Set to `true` if the `Http2Stream` instance was aborted abnormally. When set,
1382-
the `'aborted'` event will have been emitted.
1396+
`true` if the `Http2Stream` was closed while the writable side was
1397+
still open. When set, the `'aborted'` event was emitted.
13831398

13841399
#### `http2stream.bufferSize`
13851400

@@ -1723,6 +1738,13 @@ stream.on('push', (headers, flags) => {
17231738

17241739
<!-- YAML
17251740
added: v8.4.0
1741+
changes:
1742+
- version: REPLACEME
1743+
pr-url: https://github.com/nodejs/node/pull/63249
1744+
description: >-
1745+
If no `'response'` listener is attached when the response headers
1746+
arrive, the response body is now silently discarded - matching
1747+
the `lib/http` client behaviour.
17261748
-->
17271749

17281750
* `headers` {HTTP/2 Headers Object}
@@ -1744,6 +1766,16 @@ req.on('response', (headers, flags) => {
17441766
});
17451767
```
17461768

1769+
If no `'response'` listener is attached at the moment the response
1770+
arrives, the response body will be entirely discarded (the stream is
1771+
silently resumed). However, if a `'response'` listener is added, the
1772+
data from the response object **must** be consumed — either by calling
1773+
`response.read()` whenever there is a `'readable'` event, by adding a
1774+
`'data'` handler, or by calling the `.resume()` method. Until the data
1775+
is consumed, the `'end'` event will not fire. Also, until the data is
1776+
read, it will consume memory that can eventually lead to a "process
1777+
out of memory" error.
1778+
17471779
```cjs
17481780
const http2 = require('node:http2');
17491781
const client = http2.connect('https://localhost');
@@ -4035,11 +4067,8 @@ data.
40354067
added: v8.4.0
40364068
-->
40374069

4038-
The `'aborted'` event is emitted whenever a `Http2ServerRequest` instance is
4039-
abnormally aborted in mid-communication.
4040-
4041-
The `'aborted'` event will only be emitted if the `Http2ServerRequest` writable
4042-
side has not been ended.
4070+
The `'aborted'` event is emitted whenever a `Http2ServerRequest` instance
4071+
is closed while the underlying writable side is still open.
40434072

40444073
#### Event: `'close'`
40454074

@@ -5050,6 +5079,8 @@ you need to implement any fall-back behavior yourself.
50505079
[`'unknownProtocol'`]: #event-unknownprotocol
50515080
[`ClientHttp2Stream`]: #class-clienthttp2stream
50525081
[`Duplex`]: stream.md#class-streamduplex
5082+
[`ERR_HTTP2_STREAM_ABORTED`]: errors.md#err_http2_stream_aborted
5083+
[`ERR_HTTP2_STREAM_ERROR`]: errors.md#err_http2_stream_error
50535084
[`Http2ServerRequest`]: #class-http2http2serverrequest
50545085
[`Http2ServerResponse`]: #class-http2http2serverresponse
50555086
[`Http2Session` and Sockets]: #http2session-and-sockets

doc/api/process.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3171,7 +3171,7 @@ process.permission.has('fs.read');
31713171
### `process.permission.drop(scope[, reference])`
31723172
31733173
<!-- YAML
3174-
added: REPLACEME
3174+
added: v26.3.0
31753175
-->
31763176
31773177
> Stability: 1.1 - Active Development

0 commit comments

Comments
 (0)