Skip to content

Commit 2620769

Browse files
jasnelladdaleax
authored andcommitted
http2: refinement and test for socketError
Fixes: nodejs/http2#184 Refines the `'socketError'` event a bit and adds a test for the emission of the `'socketError'` event on the server. Client side is tested separately Backport-PR-URL: #14813 Backport-Reviewed-By: Anna Henningsen <[email protected]> Backport-Reviewed-By: Timothy Gu <[email protected]> PR-URL: #14239 Reviewed-By: Anna Henningsen <[email protected]> Reviewed-By: Colin Ihrig <[email protected]> Reviewed-By: Matteo Collina <[email protected]>
1 parent cd0f4c6 commit 2620769

File tree

3 files changed

+70
-29
lines changed

3 files changed

+70
-29
lines changed

doc/api/http2.md

+7-9
Original file line numberDiff line numberDiff line change
@@ -254,9 +254,9 @@ The `'socketError'` event is emitted when an `'error'` is emitted on the
254254
`Socket` instance bound to the `Http2Session`. If this event is not handled,
255255
the `'error'` event will be re-emitted on the `Socket`.
256256

257-
Likewise, when an `'error'` event is emitted on the `Http2Session`, a
258-
`'sessionError'` event will be emitted on the `Socket`. If that event is
259-
not handled, the `'error'` event will be re-emitted on the `Http2Session`.
257+
For `ServerHttp2Session` instances, a `'socketError'` event listener is always
258+
registered that will, by default, forward the event on to the owning
259+
`Http2Server` instance if no additional handlers are registered.
260260

261261
#### Event: 'timeout'
262262
<!-- YAML
@@ -1117,9 +1117,8 @@ an `Http2Session` object. If no listener is registered for this event, an
11171117
added: REPLACEME
11181118
-->
11191119

1120-
The `'socketError'` event is emitted when an `'error'` event is emitted by
1121-
a `Socket` associated with the server. If no listener is registered for this
1122-
event, an `'error'` event is emitted.
1120+
The `'socketError'` event is emitted when a `'socketError'` event is emitted by
1121+
an `Http2Session` associated with the server.
11231122

11241123
#### Event: 'stream'
11251124
<!-- YAML
@@ -1181,9 +1180,8 @@ an `Http2Session` object. If no listener is registered for this event, an
11811180
added: REPLACEME
11821181
-->
11831182

1184-
The `'socketError'` event is emitted when an `'error'` event is emitted by
1185-
a `Socket` associated with the server. If no listener is registered for this
1186-
event, an `'error'` event is emitted on the `Socket` instance instead.
1183+
The `'socketError'` event is emitted when a `'socketError'` event is emitted by
1184+
an `Http2Session` associated with the server.
11871185

11881186
#### Event: 'unknownProtocol'
11891187
<!-- YAML

lib/internal/http2/core.js

+11-20
Original file line numberDiff line numberDiff line change
@@ -2032,17 +2032,15 @@ function sessionOnError(error) {
20322032
this.emit('error', error);
20332033
}
20342034

2035-
// When a Socket emits an error, first try to forward it to the server
2036-
// as a socketError; failing that, forward it to the session as a
2035+
// When a Socket emits an error, forward it to the session as a
20372036
// socketError; failing that, remove the listener and call destroy
20382037
function socketOnError(error) {
20392038
const type = this[kSession] && this[kSession][kType];
20402039
debug(`[${sessionName(type)}] server socket error: ${error.message}`);
20412040
if (kRenegTest.test(error.message))
20422041
return this.destroy();
2043-
if (this[kServer] !== undefined && this[kServer].emit('socketError', error))
2044-
return;
2045-
if (this[kSession] !== undefined && this[kSession].emit('socketError', error))
2042+
if (this[kSession] !== undefined &&
2043+
this[kSession].emit('socketError', error, this))
20462044
return;
20472045
this.removeListener('error', socketOnError);
20482046
this.destroy(error);
@@ -2076,6 +2074,11 @@ function sessionOnPriority(stream, parent, weight, exclusive) {
20762074
this[kServer].emit('priority', stream, parent, weight, exclusive);
20772075
}
20782076

2077+
function sessionOnSocketError(error, socket) {
2078+
if (this.listenerCount('socketError') <= 1 && this[kServer] !== undefined)
2079+
this[kServer].emit('socketError', error, socket, this);
2080+
}
2081+
20792082
function connectionListener(socket) {
20802083
debug('[server] received a connection');
20812084
const options = this[kOptions] || {};
@@ -2109,6 +2112,7 @@ function connectionListener(socket) {
21092112
session.on('error', sessionOnError);
21102113
session.on('stream', sessionOnStream);
21112114
session.on('priority', sessionOnPriority);
2115+
session.on('socketError', sessionOnSocketError);
21122116

21132117
socket[kServer] = this;
21142118

@@ -2193,27 +2197,14 @@ function setupCompat(ev) {
21932197
}
21942198
}
21952199

2196-
// If the socket emits an error, forward it to the session as a socketError;
2197-
// failing that, remove the listener and destroy the socket
2198-
function clientSocketOnError(error) {
2199-
const type = this[kSession] && this[kSession][kType];
2200-
debug(`[${sessionName(type)}] client socket error: ${error.message}`);
2201-
if (kRenegTest.test(error.message))
2202-
return this.destroy();
2203-
if (this[kSession] !== undefined && this[kSession].emit('socketError', error))
2204-
return;
2205-
this.removeListener('error', clientSocketOnError);
2206-
this.destroy(error);
2207-
}
2208-
22092200
// If the session emits an error, forward it to the socket as a sessionError;
22102201
// failing that, destroy the session, remove the listener and re-emit the error
22112202
function clientSessionOnError(error) {
22122203
debug(`client session error: ${error.message}`);
22132204
if (this[kSocket] !== undefined && this[kSocket].emit('sessionError', error))
22142205
return;
22152206
this.destroy();
2216-
this.removeListener('error', clientSocketOnError);
2207+
this.removeListener('error', socketOnError);
22172208
this.removeListener('error', clientSessionOnError);
22182209
}
22192210

@@ -2249,7 +2240,7 @@ function connect(authority, options, listener) {
22492240
throw new errors.Error('ERR_HTTP2_UNSUPPORTED_PROTOCOL', protocol);
22502241
}
22512242

2252-
socket.on('error', clientSocketOnError);
2243+
socket.on('error', socketOnError);
22532244
socket.on('resume', socketOnResume);
22542245
socket.on('pause', socketOnPause);
22552246
socket.on('drain', socketOnDrain);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
// Flags: --expose-http2
2+
'use strict';
3+
4+
const common = require('../common');
5+
const assert = require('assert');
6+
const http2 = require('http2');
7+
8+
const server = http2.createServer();
9+
server.on('stream', common.mustCall((stream) => {
10+
stream.respond();
11+
stream.end('ok');
12+
}));
13+
server.on('session', common.mustCall((session) => {
14+
// First, test that the socketError event is forwarded to the session object
15+
// and not the server object.
16+
const handler = common.mustCall((error, socket) => {
17+
common.expectsError({
18+
type: Error,
19+
message: 'test'
20+
})(error);
21+
assert.strictEqual(socket, session.socket);
22+
});
23+
const isNotCalled = common.mustNotCall();
24+
session.on('socketError', handler);
25+
server.on('socketError', isNotCalled);
26+
session.socket.emit('error', new Error('test'));
27+
session.removeListener('socketError', handler);
28+
server.removeListener('socketError', isNotCalled);
29+
30+
// Second, test that the socketError is forwarded to the server object when
31+
// no socketError listener is registered for the session
32+
server.on('socketError', common.mustCall((error, socket, session) => {
33+
common.expectsError({
34+
type: Error,
35+
message: 'test'
36+
})(error);
37+
assert.strictEqual(socket, session.socket);
38+
assert.strictEqual(session, session);
39+
}));
40+
session.socket.emit('error', new Error('test'));
41+
}));
42+
43+
server.listen(0, common.mustCall(() => {
44+
const client = http2.connect(`http://localhost:${server.address().port}`);
45+
const req = client.request();
46+
req.resume();
47+
req.on('end', common.mustCall());
48+
req.on('streamClosed', common.mustCall(() => {
49+
client.destroy();
50+
server.close();
51+
}));
52+
}));

0 commit comments

Comments
 (0)