Skip to content

Commit 8bf004b

Browse files
jasnelltargos
authored andcommitted
http2: add ping event
Add a `Http2Session` event whenever a non-ack `PING` is received. Fixes: #18514 PR-URL: #23009 Reviewed-By: Anna Henningsen <[email protected]> Reviewed-By: Colin Ihrig <[email protected]> Reviewed-By: Ujjwal Sharma <[email protected]> Reviewed-By: Anatoli Papirovski <[email protected]> Reviewed-By: Denys Otrishko <[email protected]>
1 parent cb3062a commit 8bf004b

File tree

5 files changed

+81
-5
lines changed

5 files changed

+81
-5
lines changed

doc/api/http2.md

+10
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,16 @@ session.on('localSettings', (settings) => {
222222
});
223223
```
224224

225+
#### Event: 'ping'
226+
<!-- YAML
227+
added: REPLACEME
228+
-->
229+
230+
* `payload` {Buffer} The `PING` frame 8-byte payload
231+
232+
The `'ping'` event is emitted whenever a `PING` frame is received from the
233+
connected peer.
234+
225235
#### Event: 'remoteSettings'
226236
<!-- YAML
227237
added: v8.4.0

lib/internal/http2/core.js

+10
Original file line numberDiff line numberDiff line change
@@ -346,6 +346,15 @@ function submitRstStream(code) {
346346
}
347347
}
348348

349+
function onPing(payload) {
350+
const session = this[kOwner];
351+
if (session.destroyed)
352+
return;
353+
session[kUpdateTimer]();
354+
debug(`Http2Session ${sessionName(session[kType])}: new ping received`);
355+
session.emit('ping', payload);
356+
}
357+
349358
// Called when the stream is closed either by sending or receiving an
350359
// RST_STREAM frame, or through a natural end-of-stream.
351360
// If the writable and readable sides of the stream are still open at this
@@ -821,6 +830,7 @@ function setupHandle(socket, type, options) {
821830
handle.error = onSessionInternalError;
822831
handle.onpriority = onPriority;
823832
handle.onsettings = onSettings;
833+
handle.onping = onPing;
824834
handle.onheaders = onSessionHeaders;
825835
handle.onframeerror = onFrameError;
826836
handle.ongoawaydata = onGoawayData;

src/env.h

+1
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,7 @@ struct PackageConfig {
229229
V(onread_string, "onread") \
230230
V(onreadstart_string, "onreadstart") \
231231
V(onreadstop_string, "onreadstop") \
232+
V(onping_string, "onping") \
232233
V(onsettings_string, "onsettings") \
233234
V(onshutdown_string, "onshutdown") \
234235
V(onsignal_string, "onsignal") \

src/node_http2.cc

+12-5
Original file line numberDiff line numberDiff line change
@@ -1457,6 +1457,11 @@ void Http2Session::HandleOriginFrame(const nghttp2_frame* frame) {
14571457

14581458
// Called by OnFrameReceived when a complete PING frame has been received.
14591459
void Http2Session::HandlePingFrame(const nghttp2_frame* frame) {
1460+
Isolate* isolate = env()->isolate();
1461+
HandleScope scope(isolate);
1462+
Local<Context> context = env()->context();
1463+
Context::Scope context_scope(context);
1464+
Local<Value> arg;
14601465
bool ack = frame->hd.flags & NGHTTP2_FLAG_ACK;
14611466
if (ack) {
14621467
Http2Ping* ping = PopPing();
@@ -1468,13 +1473,15 @@ void Http2Session::HandlePingFrame(const nghttp2_frame* frame) {
14681473
// receive an unsolicited PING ack on a connection. Either the peer
14691474
// is buggy or malicious, and we're not going to tolerate such
14701475
// nonsense.
1471-
Isolate* isolate = env()->isolate();
1472-
HandleScope scope(isolate);
1473-
Local<Context> context = env()->context();
1474-
Context::Scope context_scope(context);
1475-
Local<Value> arg = Integer::New(isolate, NGHTTP2_ERR_PROTO);
1476+
arg = Integer::New(isolate, NGHTTP2_ERR_PROTO);
14761477
MakeCallback(env()->error_string(), 1, &arg);
14771478
}
1479+
} else {
1480+
// Notify the session that a ping occurred
1481+
arg = Buffer::Copy(env(),
1482+
reinterpret_cast<const char*>(frame->ping.opaque_data),
1483+
8).ToLocalChecked();
1484+
MakeCallback(env()->onping_string(), 1, &arg);
14781485
}
14791486
}
14801487

test/parallel/test-http2-onping.js

+48
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
'use strict';
2+
3+
const {
4+
hasCrypto,
5+
mustCall,
6+
skip
7+
} = require('../common');
8+
if (!hasCrypto)
9+
skip('missing crypto');
10+
11+
const {
12+
deepStrictEqual
13+
} = require('assert');
14+
const {
15+
createServer,
16+
connect
17+
} = require('http2');
18+
19+
const check = Buffer.from([ 1, 2, 3, 4, 5, 6, 7, 8 ]);
20+
21+
const server = createServer();
22+
server.on('stream', mustCall((stream) => {
23+
stream.respond();
24+
stream.end('ok');
25+
}));
26+
server.on('session', mustCall((session) => {
27+
session.on('ping', mustCall((payload) => {
28+
deepStrictEqual(check, payload);
29+
}));
30+
session.ping(check, mustCall());
31+
}));
32+
server.listen(0, mustCall(() => {
33+
const client = connect(`http://localhost:${server.address().port}`);
34+
35+
client.on('ping', mustCall((payload) => {
36+
deepStrictEqual(check, payload);
37+
}));
38+
client.on('connect', mustCall(() => {
39+
client.ping(check, mustCall());
40+
}));
41+
42+
const req = client.request();
43+
req.resume();
44+
req.on('close', mustCall(() => {
45+
client.close();
46+
server.close();
47+
}));
48+
}));

0 commit comments

Comments
 (0)