diff --git a/session.go b/session.go index 2703f283..b086e378 100644 --- a/session.go +++ b/session.go @@ -27,6 +27,7 @@ var ( errReopeningStreamsNotAllowed = errors.New("Reopening Streams not allowed") errRstStreamOnInvalidStream = errors.New("RST_STREAM received for unknown stream") errWindowUpdateOnInvalidStream = errors.New("WINDOW_UPDATE received for unknown stream") + errWindowUpdateOnClosedStream = errors.New("WINDOW_UPDATE received for an already closed stream") ) // StreamCallback gets a stream frame and returns a reply frame @@ -172,6 +173,8 @@ func (s *Session) run() { s.Close(err, true) // TODO: sent correct error code here case errRstStreamOnInvalidStream: utils.Errorf("Ignoring error in session: %s", err.Error()) + // Can happen when we already sent the last StreamFrame with the FinBit, but the client already sent a WindowUpdate for this Stream + case errWindowUpdateOnClosedStream: default: s.Close(err, true) } @@ -285,12 +288,16 @@ func (s *Session) handleWindowUpdateFrame(frame *frames.WindowUpdateFrame) error return nil } s.streamsMutex.RLock() - stream, ok := s.streams[frame.StreamID] - s.streamsMutex.RUnlock() + defer s.streamsMutex.RUnlock() - if !ok { + stream, streamExists := s.streams[frame.StreamID] + + if !streamExists { return errWindowUpdateOnInvalidStream } + if stream == nil { + return errWindowUpdateOnClosedStream + } stream.UpdateSendFlowControlWindow(frame.ByteOffset) diff --git a/session_test.go b/session_test.go index c2d0ba64..ef2cd5eb 100644 --- a/session_test.go +++ b/session_test.go @@ -300,6 +300,15 @@ var _ = Describe("Session", func() { }) Expect(err).To(MatchError(errWindowUpdateOnInvalidStream)) }) + + It("errors when receiving a WindowUpdateFrame for a closed stream", func() { + session.streams[5] = nil // this is what the garbageCollectStreams() does when a Stream is closed + err := session.handleWindowUpdateFrame(&frames.WindowUpdateFrame{ + StreamID: 5, + ByteOffset: 1337, + }) + Expect(err).To(MatchError(errWindowUpdateOnClosedStream)) + }) }) Context("closing", func() {