@@ -746,7 +746,8 @@ class Http2Session extends EventEmitter {
746
746
shutdown : false ,
747
747
shuttingDown : false ,
748
748
pendingAck : 0 ,
749
- maxPendingAck : Math . max ( 1 , ( options . maxPendingAck | 0 ) || 10 )
749
+ maxPendingAck : Math . max ( 1 , ( options . maxPendingAck | 0 ) || 10 ) ,
750
+ writeQueueSize : 0
750
751
} ;
751
752
752
753
this [ kType ] = type ;
@@ -1080,6 +1081,20 @@ class Http2Session extends EventEmitter {
1080
1081
}
1081
1082
1082
1083
_onTimeout ( ) {
1084
+ // This checks whether a write is currently in progress and also whether
1085
+ // that write is actually sending data across the write. The kHandle
1086
+ // stored `chunksSentSinceLastWrite` is only updated when a timeout event
1087
+ // happens, meaning that if a write is ongoing it should never equal the
1088
+ // newly fetched, updated value.
1089
+ if ( this [ kState ] . writeQueueSize > 0 ) {
1090
+ const handle = this [ kHandle ] ;
1091
+ if ( handle !== undefined &&
1092
+ handle . chunksSentSinceLastWrite !== handle . updateChunksSent ( ) ) {
1093
+ _unrefActive ( this ) ;
1094
+ return ;
1095
+ }
1096
+ }
1097
+
1083
1098
process . nextTick ( emit , this , 'timeout' ) ;
1084
1099
}
1085
1100
}
@@ -1199,12 +1214,26 @@ function createWriteReq(req, handle, data, encoding) {
1199
1214
}
1200
1215
}
1201
1216
1217
+ function trackWriteState ( stream , bytes ) {
1218
+ const session = stream [ kSession ] ;
1219
+ stream [ kState ] . writeQueueSize += bytes ;
1220
+ session [ kState ] . writeQueueSize += bytes ;
1221
+ session [ kHandle ] . chunksSentSinceLastWrite = 0 ;
1222
+ }
1223
+
1202
1224
function afterDoStreamWrite ( status , handle , req ) {
1203
1225
const session = handle [ kOwner ] ;
1204
- const stream = session [ kState ] . streams . get ( req . stream ) ;
1205
1226
_unrefActive ( session ) ;
1206
- if ( stream !== undefined )
1227
+
1228
+ const state = session [ kState ] ;
1229
+ const { bytes } = req ;
1230
+ state . writeQueueSize -= bytes ;
1231
+
1232
+ const stream = state . streams . get ( req . stream ) ;
1233
+ if ( stream !== undefined ) {
1207
1234
_unrefActive ( stream ) ;
1235
+ stream [ kState ] . writeQueueSize -= bytes ;
1236
+ }
1208
1237
1209
1238
if ( typeof req . callback === 'function' )
1210
1239
req . callback ( ) ;
@@ -1317,7 +1346,8 @@ class Http2Stream extends Duplex {
1317
1346
headersSent : false ,
1318
1347
headRequest : false ,
1319
1348
aborted : false ,
1320
- closeHandler : onSessionClose . bind ( this )
1349
+ closeHandler : onSessionClose . bind ( this ) ,
1350
+ writeQueueSize : 0
1321
1351
} ;
1322
1352
1323
1353
this . once ( 'ready' , streamOnceReady ) ;
@@ -1364,6 +1394,21 @@ class Http2Stream extends Duplex {
1364
1394
}
1365
1395
1366
1396
_onTimeout ( ) {
1397
+ // This checks whether a write is currently in progress and also whether
1398
+ // that write is actually sending data across the write. The kHandle
1399
+ // stored `chunksSentSinceLastWrite` is only updated when a timeout event
1400
+ // happens, meaning that if a write is ongoing it should never equal the
1401
+ // newly fetched, updated value.
1402
+ if ( this [ kState ] . writeQueueSize > 0 ) {
1403
+ const handle = this [ kSession ] [ kHandle ] ;
1404
+ if ( handle !== undefined &&
1405
+ handle . chunksSentSinceLastWrite !== handle . updateChunksSent ( ) ) {
1406
+ _unrefActive ( this ) ;
1407
+ _unrefActive ( this [ kSession ] ) ;
1408
+ return ;
1409
+ }
1410
+ }
1411
+
1367
1412
process . nextTick ( emit , this , 'timeout' ) ;
1368
1413
}
1369
1414
@@ -1416,6 +1461,7 @@ class Http2Stream extends Duplex {
1416
1461
const err = createWriteReq ( req , handle , data , encoding ) ;
1417
1462
if ( err )
1418
1463
throw util . _errnoException ( err , 'write' , req . error ) ;
1464
+ trackWriteState ( this , req . bytes ) ;
1419
1465
}
1420
1466
1421
1467
_writev ( data , cb ) {
@@ -1444,6 +1490,7 @@ class Http2Stream extends Duplex {
1444
1490
const err = handle . writev ( req , chunks ) ;
1445
1491
if ( err )
1446
1492
throw util . _errnoException ( err , 'write' , req . error ) ;
1493
+ trackWriteState ( this , req . bytes ) ;
1447
1494
}
1448
1495
1449
1496
_read ( nread ) {
@@ -1537,6 +1584,10 @@ class Http2Stream extends Duplex {
1537
1584
return ;
1538
1585
}
1539
1586
1587
+ const state = this [ kState ] ;
1588
+ session [ kState ] . writeQueueSize -= state . writeQueueSize ;
1589
+ state . writeQueueSize = 0 ;
1590
+
1540
1591
const server = session [ kServer ] ;
1541
1592
if ( server !== undefined && err ) {
1542
1593
server . emit ( 'streamError' , err , this ) ;
@@ -1631,7 +1682,12 @@ function processRespondWithFD(fd, headers, offset = 0, length = -1,
1631
1682
if ( ret < 0 ) {
1632
1683
err = new NghttpError ( ret ) ;
1633
1684
process . nextTick ( emit , this , 'error' , err ) ;
1685
+ break ;
1634
1686
}
1687
+ // exact length of the file doesn't matter here, since the
1688
+ // stream is closing anyway — just use 1 to signify that
1689
+ // a write does exist
1690
+ trackWriteState ( this , 1 ) ;
1635
1691
}
1636
1692
}
1637
1693
0 commit comments