Skip to content

Commit 58478d8

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 39267e8 commit 58478d8

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
@@ -1655,6 +1655,10 @@ changes:
16551655
pr-url: https://github.com/nodejs/node/pull/16676
16561656
description: Added the `maxHeaderListPairs` option with a default limit of
16571657
128 header pairs.
1658+
- version: REPLACEME
1659+
pr-url: https://github.com/nodejs/node/pull/15752
1660+
description: Added the `Http1IncomingMessage` and `Http1ServerResponse`
1661+
option.
16581662
-->
16591663

16601664
* `options` {Object}
@@ -1704,6 +1708,12 @@ changes:
17041708
used to determine the padding. See [Using options.selectPadding][].
17051709
* `settings` {HTTP2 Settings Object} The initial settings to send to the
17061710
remote peer upon connection.
1711+
* `Http1IncomingMessage` {http.IncomingMessage} Specifies the IncomingMessage
1712+
class to used for HTTP/1 fallback. Useful for extending the original
1713+
`http.IncomingMessage`. **Default:** `http.IncomingMessage`
1714+
* `Http1ServerResponse` {http.ServerResponse} Specifies the ServerResponse
1715+
class to used for HTTP/1 fallback. Useful for extending the original
1716+
`http.ServerResponse`. **Default:** `http.ServerResponse`
17071717
* `onRequestHandler` {Function} See [Compatibility API][]
17081718
* Returns: {Http2Server}
17091719

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');
@@ -2442,8 +2445,11 @@ function connectionListener(socket) {
24422445

24432446
if (socket.alpnProtocol === false || socket.alpnProtocol === 'http/1.1') {
24442447
// Fallback to HTTP/1.1
2445-
if (options.allowHTTP1 === true)
2448+
if (options.allowHTTP1 === true) {
2449+
socket.server[kIncomingMessage] = options.Http1IncomingMessage;
2450+
socket.server[kServerResponse] = options.Http1ServerResponse;
24462451
return httpConnectionListener.call(this, socket);
2452+
}
24472453
// Let event handler deal with the socket
24482454
if (!this.emit('unknownProtocol', socket))
24492455
socket.destroy();
@@ -2474,6 +2480,13 @@ function initializeOptions(options) {
24742480
options.allowHalfOpen = true;
24752481
assertIsObject(options.settings, 'options.settings');
24762482
options.settings = Object.assign({}, options.settings);
2483+
2484+
// Used only with allowHTTP1
2485+
options.Http1IncomingMessage = options.Http1IncomingMessage ||
2486+
http.IncomingMessage;
2487+
options.Http1ServerResponse = options.Http1ServerResponse ||
2488+
http.ServerResponse;
2489+
24772490
return options;
24782491
}
24792492

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)