Skip to content

Commit 0861498

Browse files
devsnekrichardlau
authored andcommitted
http2: add server handshake utility
PR-URL: #51172 Reviewed-By: Matteo Collina <[email protected]>
1 parent 2a92196 commit 0861498

File tree

5 files changed

+85
-7
lines changed

5 files changed

+85
-7
lines changed

doc/api/http2.md

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

2893+
### `http2.performServerHandshake(socket[, options])`
2894+
2895+
<!-- YAML
2896+
added: REPLACEME
2897+
-->
2898+
2899+
* `socket` {stream.Duplex}
2900+
* `options` {Object}
2901+
* ...: Any [`http2.createServer()`][] option can be provided.
2902+
* Returns: {ServerHttp2Session}
2903+
2904+
Create an HTTP/2 server session from an existing socket.
2905+
28932906
### `http2.sensitiveHeaders`
28942907

28952908
<!-- 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
@@ -1228,12 +1228,6 @@ class Http2Session extends EventEmitter {
12281228
constructor(type, options, socket) {
12291229
super();
12301230

1231-
if (!socket._handle || !socket._handle.isStreamBase) {
1232-
socket = new JSStreamSocket(socket);
1233-
}
1234-
socket.on('error', socketOnError);
1235-
socket.on('close', socketOnClose);
1236-
12371231
// No validation is performed on the input parameters because this
12381232
// constructor is not exported directly for users.
12391233

@@ -1245,6 +1239,12 @@ class Http2Session extends EventEmitter {
12451239

12461240
socket[kSession] = this;
12471241

1242+
if (!socket._handle || !socket._handle.isStreamBase) {
1243+
socket = new JSStreamSocket(socket);
1244+
}
1245+
socket.on('error', socketOnError);
1246+
socket.on('close', socketOnClose);
1247+
12481248
this[kState] = {
12491249
destroyCode: NGHTTP2_NO_ERROR,
12501250
flags: SESSION_FLAGS_PENDING,
@@ -1644,7 +1644,7 @@ class ServerHttp2Session extends Http2Session {
16441644
// not be an issue in practice. Additionally, the 'priority' event on
16451645
// server instances (or any other object) is fully undocumented.
16461646
this[kNativeFields][kSessionPriorityListenerCount] =
1647-
server.listenerCount('priority');
1647+
server ? server.listenerCount('priority') : 0;
16481648
}
16491649

16501650
get server() {
@@ -3435,6 +3435,11 @@ function getUnpackedSettings(buf, options = kEmptyObject) {
34353435
return settings;
34363436
}
34373437

3438+
function performServerHandshake(socket, options = {}) {
3439+
options = initializeOptions(options);
3440+
return new ServerHttp2Session(options, socket, undefined);
3441+
}
3442+
34383443
binding.setCallbackFunctions(
34393444
onSessionInternalError,
34403445
onPriority,
@@ -3458,6 +3463,7 @@ module.exports = {
34583463
getDefaultSettings,
34593464
getPackedSettings,
34603465
getUnpackedSettings,
3466+
performServerHandshake,
34613467
sensitiveHeaders: kSensitiveHeaders,
34623468
Http2Session,
34633469
Http2Stream,

lib/internal/js_stream_socket.js

+9
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ let debug = require('internal/util/debuglog').debuglog(
1717
);
1818
const { owner_symbol } = require('internal/async_hooks').symbols;
1919
const { ERR_STREAM_WRAP } = require('internal/errors').codes;
20+
const { kSession } = require('internal/stream_base_commons');
2021

2122
const kCurrentWriteRequest = Symbol('kCurrentWriteRequest');
2223
const kCurrentShutdownRequest = Symbol('kCurrentShutdownRequest');
@@ -263,6 +264,14 @@ class JSStreamSocket extends Socket {
263264
cb();
264265
});
265266
}
267+
268+
get [kSession]() {
269+
return this.stream[kSession];
270+
}
271+
272+
set [kSession](session) {
273+
this.stream[kSession] = session;
274+
}
266275
}
267276

268277
module.exports = JSStreamSocket;
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+
45+
assert.throws(() => {
46+
http2.performServerHandshake(socket);
47+
}, { code: 'ERR_HTTP2_SOCKET_BOUND' });
48+
}

0 commit comments

Comments
 (0)