Skip to content

Commit 67ab2b9

Browse files
committed
http2: add server handshake utility
1 parent 62ca050 commit 67ab2b9

File tree

4 files changed

+76
-7
lines changed

4 files changed

+76
-7
lines changed

doc/api/http2.md

+13
Original file line numberDiff line numberDiff line change
@@ -2875,6 +2875,19 @@ added: v8.4.0
28752875
Returns a [HTTP/2 Settings Object][] containing the deserialized settings from
28762876
the given `Buffer` as generated by `http2.getPackedSettings()`.
28772877

2878+
### `http2.performServerHandshake(socket[, options])`
2879+
2880+
<!-- YAML
2881+
added: REPLACEME
2882+
-->
2883+
2884+
* `socket` {stream.Duplex}
2885+
* `options` {Object}
2886+
* ...: Any [`http2.createServer()`][] option can be provided.
2887+
* Returns: {ServerHttp2Session}
2888+
2889+
Create an HTTP/2 server session from an existing socket.
2890+
28782891
### `http2.sensitiveHeaders`
28792892

28802893
<!-- YAML

lib/http2.js

+2
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ const {
88
getDefaultSettings,
99
getPackedSettings,
1010
getUnpackedSettings,
11+
performServerHandshake,
1112
sensitiveHeaders,
1213
Http2ServerRequest,
1314
Http2ServerResponse,
@@ -21,6 +22,7 @@ module.exports = {
2122
getDefaultSettings,
2223
getPackedSettings,
2324
getUnpackedSettings,
25+
performServerHandshake,
2426
sensitiveHeaders,
2527
Http2ServerRequest,
2628
Http2ServerResponse,

lib/internal/http2/core.js

+13-7
Original file line numberDiff line numberDiff line change
@@ -1208,12 +1208,6 @@ class Http2Session extends EventEmitter {
12081208
constructor(type, options, socket) {
12091209
super();
12101210

1211-
if (!socket._handle || !socket._handle.isStreamBase) {
1212-
socket = new JSStreamSocket(socket);
1213-
}
1214-
socket.on('error', socketOnError);
1215-
socket.on('close', socketOnClose);
1216-
12171211
// No validation is performed on the input parameters because this
12181212
// constructor is not exported directly for users.
12191213

@@ -1225,6 +1219,12 @@ class Http2Session extends EventEmitter {
12251219

12261220
socket[kSession] = this;
12271221

1222+
if (!socket._handle || !socket._handle.isStreamBase) {
1223+
socket = new JSStreamSocket(socket);
1224+
}
1225+
socket.on('error', socketOnError);
1226+
socket.on('close', socketOnClose);
1227+
12281228
this[kState] = {
12291229
destroyCode: NGHTTP2_NO_ERROR,
12301230
flags: SESSION_FLAGS_PENDING,
@@ -1624,7 +1624,7 @@ class ServerHttp2Session extends Http2Session {
16241624
// not be an issue in practice. Additionally, the 'priority' event on
16251625
// server instances (or any other object) is fully undocumented.
16261626
this[kNativeFields][kSessionPriorityListenerCount] =
1627-
server.listenerCount('priority');
1627+
server ? server.listenerCount('priority') : 0;
16281628
}
16291629

16301630
get server() {
@@ -3397,6 +3397,11 @@ function getUnpackedSettings(buf, options = kEmptyObject) {
33973397
return settings;
33983398
}
33993399

3400+
function performServerHandshake(socket, options = {}) {
3401+
options = initializeOptions(options);
3402+
return new ServerHttp2Session(options, socket, undefined);
3403+
}
3404+
34003405
binding.setCallbackFunctions(
34013406
onSessionInternalError,
34023407
onPriority,
@@ -3420,6 +3425,7 @@ module.exports = {
34203425
getDefaultSettings,
34213426
getPackedSettings,
34223427
getUnpackedSettings,
3428+
performServerHandshake,
34233429
sensitiveHeaders: kSensitiveHeaders,
34243430
Http2Session,
34253431
Http2Stream,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
'use strict';
2+
3+
const common = require('../common');
4+
5+
if (!common.hasCrypto)
6+
common.skip('missing crypto');
7+
8+
const assert = require('assert');
9+
const http2 = require('http2');
10+
const stream = require('stream');
11+
const makeDuplexPair = require('../common/duplexpair');
12+
13+
// Basic test
14+
{
15+
const { clientSide, serverSide } = makeDuplexPair();
16+
17+
const client = http2.connect('http://example.com', {
18+
createConnection: () => clientSide,
19+
});
20+
21+
const session = http2.performServerHandshake(serverSide);
22+
23+
session.on('stream', common.mustCall((stream, headers) => {
24+
assert.strictEqual(headers[':path'], '/test');
25+
stream.respond({
26+
':status': 200,
27+
});
28+
stream.end('hi!');
29+
}));
30+
31+
const req = client.request({ ':path': '/test' });
32+
req.on('response', common.mustCall());
33+
req.end();
34+
}
35+
36+
// Double bind should fail
37+
{
38+
const socket = new stream.Duplex({
39+
read() {},
40+
write() {},
41+
});
42+
43+
http2.performServerHandshake(socket);
44+
console.log(socket);
45+
assert.throws(() => {
46+
http2.performServerHandshake(socket);
47+
}, { code: 'ERR_HTTP2_SOCKET_BOUND' });
48+
}

0 commit comments

Comments
 (0)