@@ -237,7 +237,7 @@ public class Http2Channel extends AbstractFramedChannel<Http2Channel, AbstractHt
237
237
*/
238
238
private volatile int receiveWindowSize ;
239
239
240
- private final StreamCache sentRstStreams = new StreamCache ();
240
+ private final StreamCache resetStreamTracker = new StreamCache ();
241
241
242
242
243
243
public Http2Channel (StreamConnection connectedStreamChannel , String protocol , ByteBufferPool bufferPool , PooledByteBuffer data , boolean clientSide , boolean fromUpgrade , OptionMap settings ) {
@@ -470,10 +470,10 @@ protected AbstractHttp2StreamSourceChannel createChannelImpl(FrameHeaderData fra
470
470
//make sure it exists
471
471
StreamHolder existing = currentStreams .get (frameParser .streamId );
472
472
if (existing == null ) {
473
- existing = sentRstStreams .find (frameParser .streamId );
473
+ existing = resetStreamTracker .find (frameParser .streamId );
474
474
}
475
475
if (existing == null || existing .sourceClosed ) {
476
- if (existing != null || sentRstStreams .find (frameParser .streamId ) == null ) {
476
+ if (existing != null || resetStreamTracker .find (frameParser .streamId ) == null ) {
477
477
sendGoAway (ERROR_PROTOCOL_ERROR );
478
478
}
479
479
frameData .close ();
@@ -507,7 +507,7 @@ protected AbstractHttp2StreamSourceChannel createChannelImpl(FrameHeaderData fra
507
507
508
508
StreamHolder holder = currentStreams .get (frameParser .streamId );
509
509
if (holder == null ) {
510
- holder = sentRstStreams .find (frameParser .streamId );
510
+ holder = resetStreamTracker .find (frameParser .streamId );
511
511
if (holder != null ) {
512
512
holder .sourceChannel = (Http2StreamSourceChannel ) channel ;
513
513
} else {
@@ -723,7 +723,7 @@ protected void handleBrokenSinkChannel(Throwable e) {
723
723
@ Override
724
724
protected void closeSubChannels () {
725
725
closeSubChannels (currentStreams );
726
- closeSubChannels (sentRstStreams .getStreamHolders ());
726
+ closeSubChannels (resetStreamTracker .getStreamHolders ());
727
727
}
728
728
729
729
private void closeSubChannels (Map <Integer , StreamHolder > streams ) {
@@ -859,7 +859,7 @@ public void handleWindowUpdate(int streamId, int deltaWindowSize) throws IOExcep
859
859
StreamHolder holder = currentStreams .get (streamId );
860
860
Http2StreamSinkChannel stream = holder != null ? holder .sinkChannel : null ;
861
861
if (stream == null ) {
862
- if (sentRstStreams .find (streamId ) == null && isIdle (streamId )) {
862
+ if (resetStreamTracker .find (streamId ) == null && isIdle (streamId )) {
863
863
sendGoAway (ERROR_PROTOCOL_ERROR );
864
864
}
865
865
} else {
@@ -1211,7 +1211,7 @@ public void sendRstStream(int streamId, int statusCode) {
1211
1211
//no point sending if the channel is closed
1212
1212
return ;
1213
1213
}
1214
- sentRstStreams . store ( streamId , handleRstStream (streamId , false ) );
1214
+ handleRstStream (streamId , false );
1215
1215
if (UndertowLogger .REQUEST_IO_LOGGER .isDebugEnabled ()) {
1216
1216
UndertowLogger .REQUEST_IO_LOGGER .debugf (new ClosedChannelException (), "Sending rststream on channel %s stream %s" , this , streamId );
1217
1217
}
@@ -1222,6 +1222,7 @@ public void sendRstStream(int streamId, int statusCode) {
1222
1222
private StreamHolder handleRstStream (int streamId , boolean receivedRst ) {
1223
1223
final StreamHolder holder = currentStreams .remove (streamId );
1224
1224
if (holder != null ) {
1225
+ resetStreamTracker .store (streamId , holder );
1225
1226
if (streamId % 2 == (isClient () ? 1 : 0 )) {
1226
1227
sendConcurrentStreamsAtomicUpdater .getAndDecrement (this );
1227
1228
} else {
@@ -1234,24 +1235,46 @@ private StreamHolder handleRstStream(int streamId, boolean receivedRst) {
1234
1235
holder .sourceChannel .rstStream ();
1235
1236
}
1236
1237
if (receivedRst ) {
1237
- long currentTimeMillis = System . currentTimeMillis ();
1238
- // reset the window tracking
1239
- if ( currentTimeMillis - lastRstFrameMillis >= rstFramesTimeWindow ) {
1240
- lastRstFrameMillis = currentTimeMillis ;
1241
- receivedRstFramesPerWindow = 1 ;
1238
+ if ( holder . sinkChannel != null && holder . sourceChannel == null ) {
1239
+ //Server side originated, no input from client other than RST
1240
+ //this can happen on page refresh when push happens, but client
1241
+ //still has valid cache entry
1242
+ holder . resetByPeer = receivedRst ;
1242
1243
} else {
1243
- //
1244
- receivedRstFramesPerWindow ++;
1245
- if (receivedRstFramesPerWindow > maxRstFramesPerWindow ) {
1246
- sendGoAway (Http2Channel .ERROR_ENHANCE_YOUR_CALM );
1247
- UndertowLogger .REQUEST_IO_LOGGER .debugf ("Reached maximum number of rst frames %s during %s ms, sending GO_AWAY 11" , maxRstFramesPerWindow , rstFramesTimeWindow );
1248
- }
1244
+ handleRstWindow ();
1249
1245
}
1250
1246
}
1247
+ } else if (receivedRst ){
1248
+ final StreamHolder resetStream = resetStreamTracker .find (streamId );
1249
+ if (resetStream != null && resetStream .resetByPeer ) {
1250
+ //This means other side reset stream at some point.
1251
+ //depending on peer or network latency our frames might be late and
1252
+ //cause other end to flare up with RST, this RST can be safely ignored.
1253
+ //TODO: do we need to check error code?
1254
+ } else {
1255
+ handleRstWindow ();
1256
+ }
1251
1257
}
1252
1258
return holder ;
1253
1259
}
1254
1260
1261
+ private void handleRstWindow () {
1262
+ long currentTimeMillis = System .currentTimeMillis ();
1263
+ // reset the window tracking
1264
+ if (currentTimeMillis - lastRstFrameMillis >= rstFramesTimeWindow ) {
1265
+ lastRstFrameMillis = currentTimeMillis ;
1266
+ receivedRstFramesPerWindow = 1 ;
1267
+ } else {
1268
+ receivedRstFramesPerWindow ++;
1269
+ if (receivedRstFramesPerWindow > maxRstFramesPerWindow ) {
1270
+ sendGoAway (Http2Channel .ERROR_ENHANCE_YOUR_CALM );
1271
+ UndertowLogger .REQUEST_IO_LOGGER .debugf (
1272
+ "Reached maximum number of rst frames %s during %s ms, sending GO_AWAY 11" ,
1273
+ maxRstFramesPerWindow , rstFramesTimeWindow );
1274
+ }
1275
+ }
1276
+ }
1277
+
1255
1278
/**
1256
1279
* Creates a response stream to respond to the initial HTTP upgrade
1257
1280
*
@@ -1286,7 +1309,7 @@ public boolean isThisGoneAway() {
1286
1309
Http2StreamSourceChannel removeStreamSource (int streamId ) {
1287
1310
StreamHolder existing = currentStreams .get (streamId );
1288
1311
if (existing == null ) {
1289
- existing = sentRstStreams .find (streamId );
1312
+ existing = resetStreamTracker .find (streamId );
1290
1313
return existing == null ? null : existing .sourceChannel ;
1291
1314
}
1292
1315
existing .sourceClosed = true ;
@@ -1306,7 +1329,7 @@ Http2StreamSourceChannel removeStreamSource(int streamId) {
1306
1329
Http2StreamSourceChannel getIncomingStream (int streamId ) {
1307
1330
StreamHolder existing = currentStreams .get (streamId );
1308
1331
if (existing == null ){
1309
- existing = sentRstStreams .find (streamId );
1332
+ existing = resetStreamTracker .find (streamId );
1310
1333
if (existing == null ) {
1311
1334
return null ;
1312
1335
}
@@ -1351,6 +1374,10 @@ int getMaxHeaderListSize() {
1351
1374
private static final class StreamHolder {
1352
1375
boolean sourceClosed = false ;
1353
1376
boolean sinkClosed = false ;
1377
+ /**
1378
+ * This flag is set only in case of short lived server push that was reset by remote end.
1379
+ */
1380
+ boolean resetByPeer = false ;
1354
1381
Http2StreamSourceChannel sourceChannel ;
1355
1382
Http2StreamSinkChannel sinkChannel ;
1356
1383
0 commit comments