@@ -1486,16 +1486,17 @@ def _receive_frame(self, frame):
1486
1486
# I don't love using __class__ here, maybe reconsider it.
1487
1487
frames , events = self ._frame_dispatch_table [frame .__class__ ](frame )
1488
1488
except StreamClosedError as e :
1489
- # If the stream was closed by RST_STREAM, we just send a RST_STREAM
1490
- # to the remote peer. Otherwise, this is a connection error, and so
1491
- # we will re-raise to trigger one.
1492
- if self ._stream_is_closed_by_reset (e .stream_id ):
1489
+ if e ._connection_error :
1490
+ raise
1491
+ else :
1492
+ # A StreamClosedError is raised when a stream wants to send a
1493
+ # RST_STREAM frame. Since the H2Stream is the authoritative source
1494
+ # of its own state, we always respect its wishes here.
1495
+
1493
1496
f = RstStreamFrame (e .stream_id )
1494
1497
f .error_code = e .error_code
1495
1498
self ._prepare_for_sending ([f ])
1496
1499
events = e ._events
1497
- else :
1498
- raise
1499
1500
except StreamIDTooLowError as e :
1500
1501
# The stream ID seems invalid. This may happen when the closed
1501
1502
# stream has been cleaned up, or when the remote peer has opened a
@@ -1506,10 +1507,18 @@ def _receive_frame(self, frame):
1506
1507
# is either a stream error or a connection error.
1507
1508
if self ._stream_is_closed_by_reset (e .stream_id ):
1508
1509
# Closed by RST_STREAM is a stream error.
1509
- f = RstStreamFrame (e .stream_id )
1510
- f .error_code = ErrorCodes .STREAM_CLOSED
1511
- self ._prepare_for_sending ([f ])
1512
- events = []
1510
+ if self ._stream_is_closed_by_peer_reset (e .stream_id ):
1511
+ self ._closed_streams [e .stream_id ] = StreamClosedBy .SEND_RST_STREAM
1512
+
1513
+ f = RstStreamFrame (e .stream_id )
1514
+ f .error_code = ErrorCodes .STREAM_CLOSED
1515
+ self ._prepare_for_sending ([f ])
1516
+ events = []
1517
+ else :
1518
+ # Stream was closed by a local reset. A stream SHOULD NOT
1519
+ # send additional RST_STREAM frames. Ignore.
1520
+ events = []
1521
+ pass
1513
1522
elif self ._stream_is_closed_by_end (e .stream_id ):
1514
1523
# Closed by END_STREAM is a connection error.
1515
1524
raise StreamClosedError (e .stream_id )
@@ -1655,13 +1664,32 @@ def _handle_data_on_closed_stream(self, events, exc, frame):
1655
1664
"auto-emitted a WINDOW_UPDATE by %d" ,
1656
1665
frame .stream_id , conn_increment
1657
1666
)
1658
- f = RstStreamFrame (exc .stream_id )
1659
- f .error_code = exc .error_code
1660
- frames .append (f )
1661
- self .config .logger .debug (
1662
- "Stream %d already CLOSED or cleaned up - "
1663
- "auto-emitted a RST_FRAME" % frame .stream_id
1664
- )
1667
+
1668
+ send_rst_frame = False
1669
+
1670
+ if frame .stream_id in self ._closed_streams :
1671
+ closed_by = self ._closed_streams [frame .stream_id ]
1672
+
1673
+ if closed_by == StreamClosedBy .RECV_RST_STREAM :
1674
+ self ._closed_streams [frame .stream_id ] = StreamClosedBy .SEND_RST_STREAM
1675
+ send_rst_frame = True
1676
+ elif closed_by == StreamClosedBy .SEND_RST_STREAM :
1677
+ # Do not send additional RST_STREAM frames
1678
+ pass
1679
+ else :
1680
+ # Protocol error
1681
+ raise StreamClosedError (frame .stream_id )
1682
+ else :
1683
+ send_rst_frame = True
1684
+
1685
+ if send_rst_frame :
1686
+ f = RstStreamFrame (exc .stream_id )
1687
+ f .error_code = exc .error_code
1688
+ frames .append (f )
1689
+ self .config .logger .debug (
1690
+ "Stream %d already CLOSED or cleaned up - "
1691
+ "auto-emitted a RST_FRAME" % frame .stream_id
1692
+ )
1665
1693
return frames , events + exc ._events
1666
1694
1667
1695
def _receive_data_frame (self , frame ):
@@ -1677,6 +1705,8 @@ def _receive_data_frame(self, frame):
1677
1705
flow_controlled_length
1678
1706
)
1679
1707
1708
+ stream = None
1709
+
1680
1710
try :
1681
1711
stream = self ._get_stream_by_id (frame .stream_id )
1682
1712
frames , stream_events = stream .receive_data (
@@ -1685,6 +1715,11 @@ def _receive_data_frame(self, frame):
1685
1715
flow_controlled_length
1686
1716
)
1687
1717
except StreamClosedError as e :
1718
+ # If this exception originated from a yet-to-be clenaed up stream,
1719
+ # check if it should be a connection error
1720
+ if stream is not None and e ._connection_error :
1721
+ raise
1722
+
1688
1723
# This stream is either marked as CLOSED or already gone from our
1689
1724
# internal state.
1690
1725
return self ._handle_data_on_closed_stream (events , e , frame )
@@ -1962,7 +1997,7 @@ def _stream_closed_by(self, stream_id):
1962
1997
before opening this one.
1963
1998
"""
1964
1999
if stream_id in self .streams :
1965
- return self .streams [stream_id ].closed_by
2000
+ return self .streams [stream_id ].closed_by # pragma: no cover
1966
2001
if stream_id in self ._closed_streams :
1967
2002
return self ._closed_streams [stream_id ]
1968
2003
return None
@@ -1976,6 +2011,14 @@ def _stream_is_closed_by_reset(self, stream_id):
1976
2011
StreamClosedBy .RECV_RST_STREAM , StreamClosedBy .SEND_RST_STREAM
1977
2012
)
1978
2013
2014
+ def _stream_is_closed_by_peer_reset (self , stream_id ):
2015
+ """
2016
+ Returns ``True`` if the stream was closed by sending or receiving a
2017
+ RST_STREAM frame. Returns ``False`` otherwise.
2018
+ """
2019
+ return (self ._stream_closed_by (stream_id ) ==
2020
+ StreamClosedBy .RECV_RST_STREAM )
2021
+
1979
2022
def _stream_is_closed_by_end (self , stream_id ):
1980
2023
"""
1981
2024
Returns ``True`` if the stream was closed by sending or receiving an
0 commit comments