Skip to content

Commit f00aa2b

Browse files
committed
http2: add http1Options for HTTP/1 fallback configuration
1 parent 3ab9dd8 commit f00aa2b

File tree

4 files changed

+107
-12
lines changed

4 files changed

+107
-12
lines changed

doc/api/deprecations.md

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4415,6 +4415,42 @@ import { opendir } from 'node:fs/promises';
44154415
}
44164416
```
44174417
4418+
### DEP0202: `Http1IncomingMessage` and `Http1ServerResponse` options of HTTP/2 servers
4419+
4420+
<!-- YAML
4421+
changes:
4422+
- version: REPLACEME
4423+
pr-url: https://github.com/nodejs/node/pull/61713
4424+
description: Documentation-only deprecation.
4425+
-->
4426+
4427+
Type: Documentation-only
4428+
4429+
The `Http1IncomingMessage` and `Http1ServerResponse` options of
4430+
[`http2.createServer()`][] and [`http2.createSecureServer()`][] are
4431+
deprecated. Use `http1Options.IncomingMessage` and
4432+
`http1Options.ServerResponse` instead.
4433+
4434+
```cjs
4435+
// Deprecated
4436+
const server = http2.createSecureServer({
4437+
allowHTTP1: true,
4438+
Http1IncomingMessage: MyIncomingMessage,
4439+
Http1ServerResponse: MyServerResponse,
4440+
});
4441+
```
4442+
4443+
```cjs
4444+
// Use this instead
4445+
const server = http2.createSecureServer({
4446+
allowHTTP1: true,
4447+
http1Options: {
4448+
IncomingMessage: MyIncomingMessage,
4449+
ServerResponse: MyServerResponse,
4450+
},
4451+
});
4452+
```
4453+
44184454
[DEP0142]: #dep0142-repl_builtinlibs
44194455
[NIST SP 800-38D]: https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38d.pdf
44204456
[RFC 6066]: https://tools.ietf.org/html/rfc6066#section-3
@@ -4493,6 +4529,8 @@ import { opendir } from 'node:fs/promises';
44934529
[`http.ServerResponse`]: http.md#class-httpserverresponse
44944530
[`http.get()`]: http.md#httpgetoptions-callback
44954531
[`http.request()`]: http.md#httprequestoptions-callback
4532+
[`http2.createSecureServer()`]: http2.md#http2createsecureserveroptions-onrequesthandler
4533+
[`http2.createServer()`]: http2.md#http2createserveroptions-onrequesthandler
44964534
[`https.get()`]: https.md#httpsgetoptions-callback
44974535
[`https.request()`]: https.md#httpsrequestoptions-callback
44984536
[`message.connection`]: http.md#messageconnection

doc/api/http2.md

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2796,6 +2796,10 @@ Throws `ERR_INVALID_ARG_TYPE` for invalid `settings` argument.
27962796
<!-- YAML
27972797
added: v8.4.0
27982798
changes:
2799+
- version: REPLACEME
2800+
pr-url: https://github.com/nodejs/node/pull/61713
2801+
description: Added `http1Options` option. The `Http1IncomingMessage`
2802+
and `Http1ServerResponse` options are now deprecated.
27992803
- version:
28002804
- v23.0.0
28012805
- v22.10.0
@@ -2914,9 +2918,27 @@ changes:
29142918
* `Http1IncomingMessage` {http.IncomingMessage} Specifies the
29152919
`IncomingMessage` class to used for HTTP/1 fallback. Useful for extending
29162920
the original `http.IncomingMessage`. **Default:** `http.IncomingMessage`.
2921+
**Deprecated.** Use `http1Options.IncomingMessage` instead. See
2922+
[DEP0202][].
29172923
* `Http1ServerResponse` {http.ServerResponse} Specifies the `ServerResponse`
29182924
class to used for HTTP/1 fallback. Useful for extending the original
29192925
`http.ServerResponse`. **Default:** `http.ServerResponse`.
2926+
**Deprecated.** Use `http1Options.ServerResponse` instead. See
2927+
[DEP0202][].
2928+
* `http1Options` {Object} An options object for configuring the HTTP/1
2929+
fallback when `allowHTTP1` is `true`. These options are passed to the
2930+
underlying HTTP/1 server. See [`http.createServer()`][] for available
2931+
options. Among others, the following are supported:
2932+
* `IncomingMessage` {http.IncomingMessage} Specifies the
2933+
`IncomingMessage` class to use for HTTP/1 fallback.
2934+
**Default:** `http.IncomingMessage`.
2935+
* `ServerResponse` {http.ServerResponse} Specifies the `ServerResponse`
2936+
class to use for HTTP/1 fallback.
2937+
**Default:** `http.ServerResponse`.
2938+
* `keepAliveTimeout` {number} The number of milliseconds of inactivity
2939+
a server needs to wait for additional incoming data, after it has
2940+
finished writing the last response, before a socket will be destroyed.
2941+
**Default:** `5000`.
29202942
* `Http2ServerRequest` {http2.Http2ServerRequest} Specifies the
29212943
`Http2ServerRequest` class to use.
29222944
Useful for extending the original `Http2ServerRequest`.
@@ -2990,6 +3012,9 @@ server.listen(8000);
29903012
<!-- YAML
29913013
added: v8.4.0
29923014
changes:
3015+
- version: REPLACEME
3016+
pr-url: https://github.com/nodejs/node/pull/61713
3017+
description: Added `http1Options` option.
29933018
- version:
29943019
- v15.10.0
29953020
- v14.16.0
@@ -3108,6 +3133,20 @@ changes:
31083133
and trailing whitespace validation for HTTP/2 header field names and values
31093134
as per [RFC-9113](https://www.rfc-editor.org/rfc/rfc9113.html#section-8.2.1).
31103135
**Default:** `true`.
3136+
* `http1Options` {Object} An options object for configuring the HTTP/1
3137+
fallback when `allowHTTP1` is `true`. These options are passed to the
3138+
underlying HTTP/1 server. See [`http.createServer()`][] for available
3139+
options. Among others, the following are supported:
3140+
* `IncomingMessage` {http.IncomingMessage} Specifies the
3141+
`IncomingMessage` class to use for HTTP/1 fallback.
3142+
**Default:** `http.IncomingMessage`.
3143+
* `ServerResponse` {http.ServerResponse} Specifies the `ServerResponse`
3144+
class to use for HTTP/1 fallback.
3145+
**Default:** `http.ServerResponse`.
3146+
* `keepAliveTimeout` {number} The number of milliseconds of inactivity
3147+
a server needs to wait for additional incoming data, after it has
3148+
finished writing the last response, before a socket will be destroyed.
3149+
**Default:** `5000`.
31113150
* `onRequestHandler` {Function} See [Compatibility API][]
31123151
* Returns: {Http2SecureServer}
31133152

@@ -4937,6 +4976,7 @@ you need to implement any fall-back behavior yourself.
49374976
[ALPN Protocol ID]: https://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xhtml#alpn-protocol-ids
49384977
[ALPN negotiation]: #alpn-negotiation
49394978
[Compatibility API]: #compatibility-api
4979+
[DEP0202]: deprecations.md#dep0202-http1incomingmessage-and-http1serverresponse-options-of-http2-servers
49404980
[HTTP/1]: http.md
49414981
[HTTP/2]: https://tools.ietf.org/html/rfc7540
49424982
[HTTP/2 Headers Object]: #headers-object
@@ -4963,6 +5003,7 @@ you need to implement any fall-back behavior yourself.
49635003
[`Http2Stream`]: #class-http2stream
49645004
[`ServerHttp2Stream`]: #class-serverhttp2stream
49655005
[`TypeError`]: errors.md#class-typeerror
5006+
[`http.createServer()`]: http.md#httpcreateserveroptions-requestlistener
49665007
[`http2.SecureServer`]: #class-http2secureserver
49675008
[`http2.Server`]: #class-http2server
49685009
[`http2.createSecureServer()`]: #http2createsecureserveroptions-onrequesthandler

lib/internal/http2/core.js

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,12 @@ const { Duplex } = require('stream');
4848
const tls = require('tls');
4949
const { setImmediate, setTimeout, clearTimeout } = require('timers');
5050

51-
const { kIncomingMessage } = require('_http_common');
52-
const { kServerResponse, Server: HttpServer, httpServerPreClose, setupConnectionsTracking } = require('_http_server');
51+
const {
52+
Server: HttpServer,
53+
httpServerPreClose,
54+
setupConnectionsTracking,
55+
storeHTTPOptions,
56+
} = require('_http_server');
5357
const JSStreamSocket = require('internal/js_stream_socket');
5458

5559
const {
@@ -3253,8 +3257,6 @@ function connectionListener(socket) {
32533257
if (socket.alpnProtocol === false || socket.alpnProtocol === 'http/1.1') {
32543258
// Fallback to HTTP/1.1
32553259
if (options.allowHTTP1 === true) {
3256-
socket.server[kIncomingMessage] = options.Http1IncomingMessage;
3257-
socket.server[kServerResponse] = options.Http1ServerResponse;
32583260
return httpConnectionListener.call(this, socket);
32593261
}
32603262
// Let event handler deal with the socket
@@ -3336,9 +3338,18 @@ function initializeOptions(options) {
33363338
options.unknownProtocolTimeout = 10000;
33373339

33383340

3339-
// Used only with allowHTTP1
3340-
options.Http1IncomingMessage ||= http.IncomingMessage;
3341-
options.Http1ServerResponse ||= http.ServerResponse;
3341+
// Initialize http1Options bag for HTTP/1 fallback when allowHTTP1 is true.
3342+
// This bag is passed to storeHTTPOptions() to configure HTTP/1 server
3343+
// behavior (timeouts, IncomingMessage/ServerResponse classes, etc.).
3344+
options.http1Options = { ...options.http1Options };
3345+
3346+
// Backward compat: migrate deprecated top-level Http1 options (DEP0201)
3347+
if (options.Http1IncomingMessage !== undefined) {
3348+
options.http1Options.IncomingMessage ??= options.Http1IncomingMessage;
3349+
}
3350+
if (options.Http1ServerResponse !== undefined) {
3351+
options.http1Options.ServerResponse ??= options.Http1ServerResponse;
3352+
}
33423353

33433354
options.Http2ServerRequest ||= Http2ServerRequest;
33443355
options.Http2ServerResponse ||= Http2ServerResponse;
@@ -3386,9 +3397,7 @@ class Http2SecureServer extends TLSServer {
33863397
this.timeout = 0;
33873398
this.on('newListener', setupCompat);
33883399
if (options.allowHTTP1 === true) {
3389-
this.headersTimeout = 60_000; // Minimum between 60 seconds or requestTimeout
3390-
this.requestTimeout = 300_000; // 5 minutes
3391-
this.connectionsCheckingInterval = 30_000; // 30 seconds
3400+
storeHTTPOptions.call(this, { ...options, ...options.http1Options });
33923401
this.shouldUpgradeCallback = function() {
33933402
return this.listenerCount('upgrade') > 0;
33943403
};

test/parallel/test-http2-https-fallback-http-server-options.js

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@ const ca = fixtures.readKey('fake-startcom-root-cert.pem');
2020
function onRequest(request, response) {
2121
const { socket: { alpnProtocol } } = request.httpVersion === '2.0' ?
2222
request.stream.session : request;
23+
// Verify that http1Options are applied when allowHTTP1 is true
24+
if (request.httpVersion === '1.1') {
25+
assert.strictEqual(request.socket.server.keepAliveTimeout, 10000);
26+
}
2327
response.status(200);
2428
response.end(JSON.stringify({
2529
alpnProtocol,
@@ -46,8 +50,11 @@ class MyServerResponse extends http.ServerResponse {
4650
{
4751
cert,
4852
key, allowHTTP1: true,
49-
Http1IncomingMessage: MyIncomingMessage,
50-
Http1ServerResponse: MyServerResponse
53+
http1Options: {
54+
IncomingMessage: MyIncomingMessage,
55+
ServerResponse: MyServerResponse,
56+
keepAliveTimeout: 10000,
57+
},
5158
},
5259
common.mustCall(onRequest, 1)
5360
);

0 commit comments

Comments
 (0)