Skip to content

Commit 5bfc1d1

Browse files
hekikeMylesBorins
authored andcommitted
http2: add http fallback options to .createServer
This adds the Http1IncomingMessage and Http1ServerReponse options to http2.createServer(). Backport-PR-URL: #20456 PR-URL: #15752 Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Anatoli Papirovski <apapirovski@mac.com> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Evan Lucas <evanlucas@me.com>
1 parent 4994b7f commit 5bfc1d1

File tree

3 files changed

+114
-1
lines changed

3 files changed

+114
-1
lines changed

doc/api/http2.md

+10
Original file line numberDiff line numberDiff line change
@@ -1698,6 +1698,10 @@ changes:
16981698
pr-url: https://github.com/nodejs/node/pull/16676
16991699
description: Added the `maxHeaderListPairs` option with a default limit of
17001700
128 header pairs.
1701+
- version: REPLACEME
1702+
pr-url: https://github.com/nodejs/node/pull/15752
1703+
description: Added the `Http1IncomingMessage` and `Http1ServerResponse`
1704+
option.
17011705
-->
17021706

17031707
* `options` {Object}
@@ -1747,6 +1751,12 @@ changes:
17471751
used to determine the padding. See [Using options.selectPadding][].
17481752
* `settings` {HTTP/2 Settings Object} The initial settings to send to the
17491753
remote peer upon connection.
1754+
* `Http1IncomingMessage` {http.IncomingMessage} Specifies the IncomingMessage
1755+
class to used for HTTP/1 fallback. Useful for extending the original
1756+
`http.IncomingMessage`. **Default:** `http.IncomingMessage`
1757+
* `Http1ServerResponse` {http.ServerResponse} Specifies the ServerResponse
1758+
class to used for HTTP/1 fallback. Useful for extending the original
1759+
`http.ServerResponse`. **Default:** `http.ServerResponse`
17501760
* `onRequestHandler` {Function} See [Compatibility API][]
17511761
* Returns: {Http2Server}
17521762

lib/internal/http2/core.js

+14-1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
require('internal/util').assertCrypto();
66

77
const { async_id_symbol } = process.binding('async_wrap');
8+
const http = require('http');
89
const binding = process.binding('http2');
910
const assert = require('assert');
1011
const { Buffer } = require('buffer');
@@ -67,6 +68,8 @@ const NETServer = net.Server;
6768
const TLSServer = tls.Server;
6869

6970
const kInspect = require('internal/util').customInspectSymbol;
71+
const { kIncomingMessage } = require('_http_common');
72+
const { kServerResponse } = require('_http_server');
7073

7174
const kAlpnProtocol = Symbol('alpnProtocol');
7275
const kAuthority = Symbol('authority');
@@ -2485,8 +2488,11 @@ function connectionListener(socket) {
24852488

24862489
if (socket.alpnProtocol === false || socket.alpnProtocol === 'http/1.1') {
24872490
// Fallback to HTTP/1.1
2488-
if (options.allowHTTP1 === true)
2491+
if (options.allowHTTP1 === true) {
2492+
socket.server[kIncomingMessage] = options.Http1IncomingMessage;
2493+
socket.server[kServerResponse] = options.Http1ServerResponse;
24892494
return httpConnectionListener.call(this, socket);
2495+
}
24902496
// Let event handler deal with the socket
24912497
debug(`Unknown protocol from ${socket.remoteAddress}:${socket.remotePort}`);
24922498
if (!this.emit('unknownProtocol', socket)) {
@@ -2526,6 +2532,13 @@ function initializeOptions(options) {
25262532
options.allowHalfOpen = true;
25272533
assertIsObject(options.settings, 'options.settings');
25282534
options.settings = Object.assign({}, options.settings);
2535+
2536+
// Used only with allowHTTP1
2537+
options.Http1IncomingMessage = options.Http1IncomingMessage ||
2538+
http.IncomingMessage;
2539+
options.Http1ServerResponse = options.Http1ServerResponse ||
2540+
http.ServerResponse;
2541+
25292542
return options;
25302543
}
25312544

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
// Flags: --expose-http2
2+
'use strict';
3+
4+
const common = require('../common');
5+
const fixtures = require('../common/fixtures');
6+
7+
if (!common.hasCrypto)
8+
common.skip('missing crypto');
9+
10+
const assert = require('assert');
11+
const url = require('url');
12+
const tls = require('tls');
13+
const http2 = require('http2');
14+
const https = require('https');
15+
const http = require('http');
16+
17+
const key = fixtures.readKey('agent8-key.pem');
18+
const cert = fixtures.readKey('agent8-cert.pem');
19+
const ca = fixtures.readKey('fake-startcom-root-cert.pem');
20+
21+
function onRequest(request, response) {
22+
const { socket: { alpnProtocol } } = request.httpVersion === '2.0' ?
23+
request.stream.session : request;
24+
response.status(200);
25+
response.end(JSON.stringify({
26+
alpnProtocol,
27+
httpVersion: request.httpVersion,
28+
userAgent: request.getUserAgent()
29+
}));
30+
}
31+
32+
class MyIncomingMessage extends http.IncomingMessage {
33+
getUserAgent() {
34+
return this.headers['user-agent'] || 'unknown';
35+
}
36+
}
37+
38+
class MyServerResponse extends http.ServerResponse {
39+
status(code) {
40+
return this.writeHead(code, { 'Content-Type': 'application/json' });
41+
}
42+
}
43+
44+
// HTTP/2 & HTTP/1.1 server
45+
{
46+
const server = http2.createSecureServer(
47+
{
48+
cert,
49+
key, allowHTTP1: true,
50+
Http1IncomingMessage: MyIncomingMessage,
51+
Http1ServerResponse: MyServerResponse
52+
},
53+
common.mustCall(onRequest, 1)
54+
);
55+
56+
server.listen(0);
57+
58+
server.on('listening', common.mustCall(() => {
59+
const { port } = server.address();
60+
const origin = `https://localhost:${port}`;
61+
62+
// HTTP/1.1 client
63+
https.get(
64+
Object.assign(url.parse(origin), {
65+
secureContext: tls.createSecureContext({ ca }),
66+
headers: { 'User-Agent': 'node-test' }
67+
}),
68+
common.mustCall((response) => {
69+
assert.strictEqual(response.statusCode, 200);
70+
assert.strictEqual(response.statusMessage, 'OK');
71+
assert.strictEqual(
72+
response.headers['content-type'],
73+
'application/json'
74+
);
75+
76+
response.setEncoding('utf8');
77+
let raw = '';
78+
response.on('data', (chunk) => { raw += chunk; });
79+
response.on('end', common.mustCall(() => {
80+
const { alpnProtocol, httpVersion, userAgent } = JSON.parse(raw);
81+
assert.strictEqual(alpnProtocol, false);
82+
assert.strictEqual(httpVersion, '1.1');
83+
assert.strictEqual(userAgent, 'node-test');
84+
85+
server.close();
86+
}));
87+
})
88+
);
89+
}));
90+
}

0 commit comments

Comments
 (0)