@@ -167,6 +167,7 @@ const kID = Symbol('id');
167
167
const kInit = Symbol ( 'init' ) ;
168
168
const kInfoHeaders = Symbol ( 'sent-info-headers' ) ;
169
169
const kLocalSettings = Symbol ( 'local-settings' ) ;
170
+ const kNativeFields = Symbol ( 'kNativeFields' ) ;
170
171
const kOptions = Symbol ( 'options' ) ;
171
172
const kOwner = owner_symbol ;
172
173
const kOrigin = Symbol ( 'origin' ) ;
@@ -188,7 +189,15 @@ const {
188
189
paddingBuffer,
189
190
PADDING_BUF_FRAME_LENGTH ,
190
191
PADDING_BUF_MAX_PAYLOAD_LENGTH ,
191
- PADDING_BUF_RETURN_VALUE
192
+ PADDING_BUF_RETURN_VALUE ,
193
+ kBitfield,
194
+ kSessionPriorityListenerCount,
195
+ kSessionFrameErrorListenerCount,
196
+ kSessionUint8FieldCount,
197
+ kSessionHasRemoteSettingsListeners,
198
+ kSessionRemoteSettingsIsUpToDate,
199
+ kSessionHasPingListeners,
200
+ kSessionHasAltsvcListeners,
192
201
} = binding ;
193
202
194
203
const {
@@ -364,6 +373,76 @@ function submitRstStream(code) {
364
373
}
365
374
}
366
375
376
+ // Keep track of the number/presence of JS event listeners. Knowing that there
377
+ // are no listeners allows the C++ code to skip calling into JS for an event.
378
+ function sessionListenerAdded ( name ) {
379
+ switch ( name ) {
380
+ case 'ping' :
381
+ this [ kNativeFields ] [ kBitfield ] |= 1 << kSessionHasPingListeners ;
382
+ break ;
383
+ case 'altsvc' :
384
+ this [ kNativeFields ] [ kBitfield ] |= 1 << kSessionHasAltsvcListeners ;
385
+ break ;
386
+ case 'remoteSettings' :
387
+ this [ kNativeFields ] [ kBitfield ] |= 1 << kSessionHasRemoteSettingsListeners ;
388
+ break ;
389
+ case 'priority' :
390
+ this [ kNativeFields ] [ kSessionPriorityListenerCount ] ++ ;
391
+ break ;
392
+ case 'frameError' :
393
+ this [ kNativeFields ] [ kSessionFrameErrorListenerCount ] ++ ;
394
+ break ;
395
+ }
396
+ }
397
+
398
+ function sessionListenerRemoved ( name ) {
399
+ switch ( name ) {
400
+ case 'ping' :
401
+ if ( this . listenerCount ( name ) > 0 ) return ;
402
+ this [ kNativeFields ] [ kBitfield ] &= ~ ( 1 << kSessionHasPingListeners ) ;
403
+ break ;
404
+ case 'altsvc' :
405
+ if ( this . listenerCount ( name ) > 0 ) return ;
406
+ this [ kNativeFields ] [ kBitfield ] &= ~ ( 1 << kSessionHasAltsvcListeners ) ;
407
+ break ;
408
+ case 'remoteSettings' :
409
+ if ( this . listenerCount ( name ) > 0 ) return ;
410
+ this [ kNativeFields ] [ kBitfield ] &=
411
+ ~ ( 1 << kSessionHasRemoteSettingsListeners ) ;
412
+ break ;
413
+ case 'priority' :
414
+ this [ kNativeFields ] [ kSessionPriorityListenerCount ] -- ;
415
+ break ;
416
+ case 'frameError' :
417
+ this [ kNativeFields ] [ kSessionFrameErrorListenerCount ] -- ;
418
+ break ;
419
+ }
420
+ }
421
+
422
+ // Also keep track of listeners for the Http2Stream instances, as some events
423
+ // are emitted on those objects.
424
+ function streamListenerAdded ( name ) {
425
+ switch ( name ) {
426
+ case 'priority' :
427
+ this [ kSession ] [ kNativeFields ] [ kSessionPriorityListenerCount ] ++ ;
428
+ break ;
429
+ case 'frameError' :
430
+ this [ kSession ] [ kNativeFields ] [ kSessionFrameErrorListenerCount ] ++ ;
431
+ break ;
432
+ }
433
+ }
434
+
435
+ function streamListenerRemoved ( name ) {
436
+ switch ( name ) {
437
+ case 'priority' :
438
+ this [ kSession ] [ kNativeFields ] [ kSessionPriorityListenerCount ] -- ;
439
+ break ;
440
+ case 'frameError' :
441
+ this [ kSession ] [ kNativeFields ] [ kSessionFrameErrorListenerCount ] -- ;
442
+ break ;
443
+ }
444
+ }
445
+
367
446
function onPing ( payload ) {
368
447
const session = this [ kOwner ] ;
369
448
if ( session . destroyed )
@@ -421,7 +500,6 @@ function onSettings() {
421
500
return ;
422
501
session [ kUpdateTimer ] ( ) ;
423
502
debugSessionObj ( session , 'new settings received' ) ;
424
- session [ kRemoteSettings ] = undefined ;
425
503
session . emit ( 'remoteSettings' , session . remoteSettings ) ;
426
504
}
427
505
@@ -863,6 +941,10 @@ function setupHandle(socket, type, options) {
863
941
handle . consume ( socket . _handle . _externalStream ) ;
864
942
865
943
this [ kHandle ] = handle ;
944
+ if ( this [ kNativeFields ] )
945
+ handle . fields . set ( this [ kNativeFields ] ) ;
946
+ else
947
+ this [ kNativeFields ] = handle . fields ;
866
948
867
949
if ( socket . encrypted ) {
868
950
this [ kAlpnProtocol ] = socket . alpnProtocol ;
@@ -904,6 +986,7 @@ function finishSessionDestroy(session, error) {
904
986
session [ kProxySocket ] = undefined ;
905
987
session [ kSocket ] = undefined ;
906
988
session [ kHandle ] = undefined ;
989
+ session [ kNativeFields ] = new Uint8Array ( kSessionUint8FieldCount ) ;
907
990
socket [ kSession ] = undefined ;
908
991
socket [ kServer ] = undefined ;
909
992
@@ -982,6 +1065,7 @@ class Http2Session extends EventEmitter {
982
1065
this [ kProxySocket ] = null ;
983
1066
this [ kSocket ] = socket ;
984
1067
this [ kTimeout ] = null ;
1068
+ this [ kHandle ] = undefined ;
985
1069
986
1070
// Do not use nagle's algorithm
987
1071
if ( typeof socket . setNoDelay === 'function' )
@@ -1000,6 +1084,11 @@ class Http2Session extends EventEmitter {
1000
1084
setupFn ( ) ;
1001
1085
}
1002
1086
1087
+ if ( ! this [ kNativeFields ] )
1088
+ this [ kNativeFields ] = new Uint8Array ( kSessionUint8FieldCount ) ;
1089
+ this . on ( 'newListener' , sessionListenerAdded ) ;
1090
+ this . on ( 'removeListener' , sessionListenerRemoved ) ;
1091
+
1003
1092
debugSession ( type , 'created' ) ;
1004
1093
}
1005
1094
@@ -1154,13 +1243,18 @@ class Http2Session extends EventEmitter {
1154
1243
1155
1244
// The settings currently in effect for the remote peer.
1156
1245
get remoteSettings ( ) {
1157
- const settings = this [ kRemoteSettings ] ;
1158
- if ( settings !== undefined )
1159
- return settings ;
1246
+ if ( this [ kNativeFields ] [ kBitfield ] &
1247
+ ( 1 << kSessionRemoteSettingsIsUpToDate ) ) {
1248
+ const settings = this [ kRemoteSettings ] ;
1249
+ if ( settings !== undefined ) {
1250
+ return settings ;
1251
+ }
1252
+ }
1160
1253
1161
1254
if ( this . destroyed || this . connecting )
1162
1255
return { } ;
1163
1256
1257
+ this [ kNativeFields ] [ kBitfield ] |= ( 1 << kSessionRemoteSettingsIsUpToDate ) ;
1164
1258
return this [ kRemoteSettings ] = getSettings ( this [ kHandle ] , true ) ; // Remote
1165
1259
}
1166
1260
@@ -1343,6 +1437,12 @@ class ServerHttp2Session extends Http2Session {
1343
1437
constructor ( options , socket , server ) {
1344
1438
super ( NGHTTP2_SESSION_SERVER , options , socket ) ;
1345
1439
this [ kServer ] = server ;
1440
+ // This is a bit inaccurate because it does not reflect changes to
1441
+ // number of listeners made after the session was created. This should
1442
+ // not be an issue in practice. Additionally, the 'priority' event on
1443
+ // server instances (or any other object) is fully undocumented.
1444
+ this [ kNativeFields ] [ kSessionPriorityListenerCount ] =
1445
+ server . listenerCount ( 'priority' ) ;
1346
1446
}
1347
1447
1348
1448
get server ( ) {
@@ -1660,6 +1760,9 @@ class Http2Stream extends Duplex {
1660
1760
} ;
1661
1761
1662
1762
this . on ( 'pause' , streamOnPause ) ;
1763
+
1764
+ this . on ( 'newListener' , streamListenerAdded ) ;
1765
+ this . on ( 'removeListener' , streamListenerRemoved ) ;
1663
1766
}
1664
1767
1665
1768
[ kUpdateTimer ] ( ) {
@@ -2633,7 +2736,7 @@ function sessionOnPriority(stream, parent, weight, exclusive) {
2633
2736
}
2634
2737
2635
2738
function sessionOnError ( error ) {
2636
- if ( this [ kServer ] )
2739
+ if ( this [ kServer ] !== undefined )
2637
2740
this [ kServer ] . emit ( 'sessionError' , error , this ) ;
2638
2741
}
2639
2742
@@ -2682,8 +2785,10 @@ function connectionListener(socket) {
2682
2785
const session = new ServerHttp2Session ( options , socket , this ) ;
2683
2786
2684
2787
session . on ( 'stream' , sessionOnStream ) ;
2685
- session . on ( 'priority' , sessionOnPriority ) ;
2686
2788
session . on ( 'error' , sessionOnError ) ;
2789
+ // Don't count our own internal listener.
2790
+ session . on ( 'priority' , sessionOnPriority ) ;
2791
+ session [ kNativeFields ] [ kSessionPriorityListenerCount ] -- ;
2687
2792
2688
2793
if ( this . timeout )
2689
2794
session . setTimeout ( this . timeout , sessionOnTimeout ) ;
0 commit comments