Skip to content

Commit 3e5b07a

Browse files
jasnelladdaleax
authored andcommitted
http2: refactor trailers API
Rather than using the `'fetchTrailers'` event to collect trailers, a new `getTrailers` callback option is supported. If not set, the internals will skip calling out for trailers at all. Expands the test to make sure trailers work from the client side also. Backport-PR-URL: #14813 Backport-Reviewed-By: Anna Henningsen <[email protected]> Backport-Reviewed-By: Timothy Gu <[email protected]> PR-URL: #14239 Reviewed-By: Anna Henningsen <[email protected]> Reviewed-By: Colin Ihrig <[email protected]> Reviewed-By: Matteo Collina <[email protected]>
1 parent 26e1f8e commit 3e5b07a

File tree

8 files changed

+272
-90
lines changed

8 files changed

+272
-90
lines changed

doc/api/http2.md

+100-25
Original file line numberDiff line numberDiff line change
@@ -346,6 +346,9 @@ added: REPLACEME
346346
* `weight` {number} Specifies the relative dependency of a stream in relation
347347
to other streams with the same `parent`. The value is a number between `1`
348348
and `256` (inclusive).
349+
* `getTrailers` {Function} Callback function invoked to collect trailer
350+
headers.
351+
349352
* Returns: {ClientHttp2Stream}
350353

351354
For HTTP/2 Client `Http2Session` instances only, the `http2session.request()`
@@ -371,6 +374,16 @@ req.on('response', (headers) => {
371374
});
372375
```
373376

377+
When set, the `options.getTrailers()` function is called immediately after
378+
queuing the last chunk of payload data to be sent. The callback is passed a
379+
single object (with a `null` prototype) that the listener may used to specify
380+
the trailing header fields to send to the peer.
381+
382+
*Note*: The HTTP/1 specification forbids trailers from containing HTTP/2
383+
"pseudo-header" fields (e.g. `':method'`, `':path'`, etc). An `'error'` event
384+
will be emitted if the `getTrailers` callback attempts to set such header
385+
fields.
386+
374387
#### http2session.rstStream(stream, code)
375388
<!-- YAML
376389
added: REPLACEME
@@ -617,27 +630,6 @@ added: REPLACEME
617630
The `'error'` event is emitted when an error occurs during the processing of
618631
an `Http2Stream`.
619632

620-
#### Event: 'fetchTrailers'
621-
<!-- YAML
622-
added: REPLACEME
623-
-->
624-
625-
The `'fetchTrailers'` event is emitted by the `Http2Stream` immediately after
626-
queuing the last chunk of payload data to be sent. The listener callback is
627-
passed a single object (with a `null` prototype) that the listener may used
628-
to specify the trailing header fields to send to the peer.
629-
630-
```js
631-
stream.on('fetchTrailers', (trailers) => {
632-
trailers['ABC'] = 'some value to send';
633-
});
634-
```
635-
636-
*Note*: The HTTP/1 specification forbids trailers from containing HTTP/2
637-
"pseudo-header" fields (e.g. `':status'`, `':path'`, etc). An `'error'` event
638-
will be emitted if the `'fetchTrailers'` event handler attempts to set such
639-
header fields.
640-
641633
#### Event: 'frameError'
642634
<!-- YAML
643635
added: REPLACEME
@@ -991,6 +983,8 @@ added: REPLACEME
991983
* `options` {Object}
992984
* `endStream` {boolean} Set to `true` to indicate that the response will not
993985
include payload data.
986+
* `getTrailers` {function} Callback function invoked to collect trailer
987+
headers.
994988
* Returns: {undefined}
995989

996990
```js
@@ -1002,6 +996,29 @@ server.on('stream', (stream) => {
1002996
});
1003997
```
1004998

999+
When set, the `options.getTrailers()` function is called immediately after
1000+
queuing the last chunk of payload data to be sent. The callback is passed a
1001+
single object (with a `null` prototype) that the listener may used to specify
1002+
the trailing header fields to send to the peer.
1003+
1004+
```js
1005+
const http2 = require('http2');
1006+
const server = http2.createServer();
1007+
server.on('stream', (stream) => {
1008+
stream.respond({ ':status': 200 }, {
1009+
getTrailers(trailers) {
1010+
trailers['ABC'] = 'some value to send';
1011+
}
1012+
});
1013+
stream.end('some data');
1014+
});
1015+
```
1016+
1017+
*Note*: The HTTP/1 specification forbids trailers from containing HTTP/2
1018+
"pseudo-header" fields (e.g. `':status'`, `':path'`, etc). An `'error'` event
1019+
will be emitted if the `getTrailers` callback attempts to set such header
1020+
fields.
1021+
10051022
#### http2stream.respondWithFD(fd[, headers[, options]])
10061023
<!-- YAML
10071024
added: REPLACEME
@@ -1011,6 +1028,8 @@ added: REPLACEME
10111028
* `headers` {[Headers Object][]}
10121029
* `options` {Object}
10131030
* `statCheck` {Function}
1031+
* `getTrailers` {Function} Callback function invoked to collect trailer
1032+
headers.
10141033
* `offset` {number} The offset position at which to begin reading
10151034
* `length` {number} The amount of data from the fd to send
10161035

@@ -1020,8 +1039,7 @@ attempting to read data using the file descriptor, the `Http2Stream` will be
10201039
closed using an `RST_STREAM` frame using the standard `INTERNAL_ERROR` code.
10211040

10221041
When used, the `Http2Stream` object's Duplex interface will be closed
1023-
automatically. HTTP trailer fields cannot be sent. The `'fetchTrailers'` event
1024-
will *not* be emitted.
1042+
automatically.
10251043

10261044
```js
10271045
const http2 = require('http2');
@@ -1052,6 +1070,39 @@ The `offset` and `length` options may be used to limit the response to a
10521070
specific range subset. This can be used, for instance, to support HTTP Range
10531071
requests.
10541072

1073+
When set, the `options.getTrailers()` function is called immediately after
1074+
queuing the last chunk of payload data to be sent. The callback is passed a
1075+
single object (with a `null` prototype) that the listener may used to specify
1076+
the trailing header fields to send to the peer.
1077+
1078+
```js
1079+
const http2 = require('http2');
1080+
const fs = require('fs');
1081+
1082+
const fd = fs.openSync('/some/file', 'r');
1083+
1084+
const server = http2.createServer();
1085+
server.on('stream', (stream) => {
1086+
const stat = fs.fstatSync(fd);
1087+
const headers = {
1088+
'content-length': stat.size,
1089+
'last-modified': stat.mtime.toUTCString(),
1090+
'content-type': 'text/plain'
1091+
};
1092+
stream.respondWithFD(fd, headers, {
1093+
getTrailers(trailers) {
1094+
trailers['ABC'] = 'some value to send';
1095+
}
1096+
});
1097+
});
1098+
server.on('close', () => fs.closeSync(fd));
1099+
```
1100+
1101+
*Note*: The HTTP/1 specification forbids trailers from containing HTTP/2
1102+
"pseudo-header" fields (e.g. `':status'`, `':path'`, etc). An `'error'` event
1103+
will be emitted if the `getTrailers` callback attempts to set such header
1104+
fields.
1105+
10551106
#### http2stream.respondWithFile(path[, headers[, options]])
10561107
<!-- YAML
10571108
added: REPLACEME
@@ -1061,15 +1112,16 @@ added: REPLACEME
10611112
* `headers` {[Headers Object][]}
10621113
* `options` {Object}
10631114
* `statCheck` {Function}
1115+
* `getTrailers` {Function} Callback function invoked to collect trailer
1116+
headers.
10641117
* `offset` {number} The offset position at which to begin reading
10651118
* `length` {number} The amount of data from the fd to send
10661119

10671120
Sends a regular file as the response. The `path` must specify a regular file
10681121
or an `'error'` event will be emitted on the `Http2Stream` object.
10691122

10701123
When used, the `Http2Stream` object's Duplex interface will be closed
1071-
automatically. HTTP trailer fields cannot be sent. The `'fetchTrailers'` event
1072-
will *not* be emitted.
1124+
automatically.
10731125

10741126
The optional `options.statCheck` function may be specified to give user code
10751127
an opportunity to set additional content headers based on the `fs.Stat` details
@@ -1120,6 +1172,29 @@ The `offset` and `length` options may be used to limit the response to a
11201172
specific range subset. This can be used, for instance, to support HTTP Range
11211173
requests.
11221174

1175+
When set, the `options.getTrailers()` function is called immediately after
1176+
queuing the last chunk of payload data to be sent. The callback is passed a
1177+
single object (with a `null` prototype) that the listener may used to specify
1178+
the trailing header fields to send to the peer.
1179+
1180+
```js
1181+
const http2 = require('http2');
1182+
const server = http2.createServer();
1183+
server.on('stream', (stream) => {
1184+
function getTrailers(trailers) {
1185+
trailers['ABC'] = 'some value to send';
1186+
}
1187+
stream.respondWithFile('/some/file',
1188+
{ 'content-type': 'text/plain' },
1189+
{ getTrailers });
1190+
});
1191+
```
1192+
1193+
*Note*: The HTTP/1 specification forbids trailers from containing HTTP/2
1194+
"pseudo-header" fields (e.g. `':status'`, `':path'`, etc). An `'error'` event
1195+
will be emitted if the `getTrailers` callback attempts to set such header
1196+
fields.
1197+
11231198
### Class: Http2Server
11241199
<!-- YAML
11251200
added: REPLACEME

0 commit comments

Comments
 (0)