@@ -167,6 +167,7 @@ const kID = Symbol('id');
167167const  kInit  =  Symbol ( 'init' ) ; 
168168const  kInfoHeaders  =  Symbol ( 'sent-info-headers' ) ; 
169169const  kLocalSettings  =  Symbol ( 'local-settings' ) ; 
170+ const  kNativeFields  =  Symbol ( 'kNativeFields' ) ; 
170171const  kOptions  =  Symbol ( 'options' ) ; 
171172const  kOwner  =  owner_symbol ; 
172173const  kOrigin  =  Symbol ( 'origin' ) ; 
@@ -188,7 +189,15 @@ const {
188189  paddingBuffer, 
189190  PADDING_BUF_FRAME_LENGTH , 
190191  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, 
192201}  =  binding ; 
193202
194203const  { 
@@ -364,6 +373,76 @@ function submitRstStream(code) {
364373  } 
365374} 
366375
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+ 
367446function  onPing ( payload )  { 
368447  const  session  =  this [ kOwner ] ; 
369448  if  ( session . destroyed ) 
@@ -421,7 +500,6 @@ function onSettings() {
421500    return ; 
422501  session [ kUpdateTimer ] ( ) ; 
423502  debugSessionObj ( session ,  'new settings received' ) ; 
424-   session [ kRemoteSettings ]  =  undefined ; 
425503  session . emit ( 'remoteSettings' ,  session . remoteSettings ) ; 
426504} 
427505
@@ -863,6 +941,10 @@ function setupHandle(socket, type, options) {
863941  handle . consume ( socket . _handle . _externalStream ) ; 
864942
865943  this [ kHandle ]  =  handle ; 
944+   if  ( this [ kNativeFields ] ) 
945+     handle . fields . set ( this [ kNativeFields ] ) ; 
946+   else 
947+     this [ kNativeFields ]  =  handle . fields ; 
866948
867949  if  ( socket . encrypted )  { 
868950    this [ kAlpnProtocol ]  =  socket . alpnProtocol ; 
@@ -904,6 +986,7 @@ function finishSessionDestroy(session, error) {
904986  session [ kProxySocket ]  =  undefined ; 
905987  session [ kSocket ]  =  undefined ; 
906988  session [ kHandle ]  =  undefined ; 
989+   session [ kNativeFields ]  =  new  Uint8Array ( kSessionUint8FieldCount ) ; 
907990  socket [ kSession ]  =  undefined ; 
908991  socket [ kServer ]  =  undefined ; 
909992
@@ -982,6 +1065,7 @@ class Http2Session extends EventEmitter {
9821065    this [ kProxySocket ]  =  null ; 
9831066    this [ kSocket ]  =  socket ; 
9841067    this [ kTimeout ]  =  null ; 
1068+     this [ kHandle ]  =  undefined ; 
9851069
9861070    // Do not use nagle's algorithm 
9871071    if  ( typeof  socket . setNoDelay  ===  'function' ) 
@@ -1000,6 +1084,11 @@ class Http2Session extends EventEmitter {
10001084      setupFn ( ) ; 
10011085    } 
10021086
1087+     if  ( ! this [ kNativeFields ] ) 
1088+       this [ kNativeFields ]  =  new  Uint8Array ( kSessionUint8FieldCount ) ; 
1089+     this . on ( 'newListener' ,  sessionListenerAdded ) ; 
1090+     this . on ( 'removeListener' ,  sessionListenerRemoved ) ; 
1091+ 
10031092    debugSession ( type ,  'created' ) ; 
10041093  } 
10051094
@@ -1154,13 +1243,18 @@ class Http2Session extends EventEmitter {
11541243
11551244  // The settings currently in effect for the remote peer. 
11561245  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+     } 
11601253
11611254    if  ( this . destroyed  ||  this . connecting ) 
11621255      return  { } ; 
11631256
1257+     this [ kNativeFields ] [ kBitfield ]  |=  ( 1  <<  kSessionRemoteSettingsIsUpToDate ) ; 
11641258    return  this [ kRemoteSettings ]  =  getSettings ( this [ kHandle ] ,  true ) ;  // Remote 
11651259  } 
11661260
@@ -1343,6 +1437,12 @@ class ServerHttp2Session extends Http2Session {
13431437  constructor ( options ,  socket ,  server )  { 
13441438    super ( NGHTTP2_SESSION_SERVER ,  options ,  socket ) ; 
13451439    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' ) ; 
13461446  } 
13471447
13481448  get  server ( )  { 
@@ -1660,6 +1760,9 @@ class Http2Stream extends Duplex {
16601760    } ; 
16611761
16621762    this . on ( 'pause' ,  streamOnPause ) ; 
1763+ 
1764+     this . on ( 'newListener' ,  streamListenerAdded ) ; 
1765+     this . on ( 'removeListener' ,  streamListenerRemoved ) ; 
16631766  } 
16641767
16651768  [ kUpdateTimer ] ( )  { 
@@ -2633,7 +2736,7 @@ function sessionOnPriority(stream, parent, weight, exclusive) {
26332736} 
26342737
26352738function  sessionOnError ( error )  { 
2636-   if  ( this [ kServer ] ) 
2739+   if  ( this [ kServer ]   !==   undefined ) 
26372740    this [ kServer ] . emit ( 'sessionError' ,  error ,  this ) ; 
26382741} 
26392742
@@ -2682,8 +2785,10 @@ function connectionListener(socket) {
26822785  const  session  =  new  ServerHttp2Session ( options ,  socket ,  this ) ; 
26832786
26842787  session . on ( 'stream' ,  sessionOnStream ) ; 
2685-   session . on ( 'priority' ,  sessionOnPriority ) ; 
26862788  session . on ( 'error' ,  sessionOnError ) ; 
2789+   // Don't count our own internal listener. 
2790+   session . on ( 'priority' ,  sessionOnPriority ) ; 
2791+   session [ kNativeFields ] [ kSessionPriorityListenerCount ] -- ; 
26872792
26882793  if  ( this . timeout ) 
26892794    session . setTimeout ( this . timeout ,  sessionOnTimeout ) ; 
0 commit comments