diff --git a/closed_session.go b/closed_session.go index 1ab000bf..3a0728f1 100644 --- a/closed_session.go +++ b/closed_session.go @@ -7,6 +7,9 @@ import ( "github.com/lucas-clemente/quic-go/internal/utils" ) +// A closedLocalSession is a session that we closed locally. +// When receiving packets for such a session, we need to retransmit the packet containing the CONNECTION_CLOSE frame, +// with an exponential backoff. type closedLocalSession struct { conn connection connClosePacket []byte @@ -90,3 +93,21 @@ func (s *closedLocalSession) destroy(error) { func (s *closedLocalSession) getPerspective() protocol.Perspective { return s.perspective } + +// A closedRemoteSession is a session that was closed remotely. +// For such a session, we might receive reordered packets that were sent before the CONNECTION_CLOSE. +// We can just ignore those packets. +type closedRemoteSession struct { + perspective protocol.Perspective +} + +var _ packetHandler = &closedRemoteSession{} + +func newClosedRemoteSession(pers protocol.Perspective) packetHandler { + return &closedRemoteSession{perspective: pers} +} + +func (s *closedRemoteSession) handlePacket(*receivedPacket) {} +func (s *closedRemoteSession) Close() error { return nil } +func (s *closedRemoteSession) destroy(error) {} +func (s *closedRemoteSession) getPerspective() protocol.Perspective { return s.perspective } diff --git a/session.go b/session.go index c0ccbdcb..baf2ad85 100644 --- a/session.go +++ b/session.go @@ -954,7 +954,7 @@ func (s *session) closeForRecreating() protocol.PacketNumber { func (s *session) closeRemote(e error) { s.closeOnce.Do(func() { s.logger.Errorf("Peer closed session with error: %s", e) - s.sessionRunner.Remove(s.srcConnID) + s.sessionRunner.ReplaceWithClosed(s.srcConnID, newClosedRemoteSession(s.perspective)) s.closeChan <- closeError{err: e, remote: true} }) } diff --git a/session_test.go b/session_test.go index 5c92a4b2..c5dd26e9 100644 --- a/session_test.go +++ b/session_test.go @@ -93,6 +93,7 @@ var _ = Describe("Session", func() { expectReplaceWithClosed := func() { sessionRunner.EXPECT().ReplaceWithClosed(sess.srcConnID, gomock.Any()).Do(func(_ protocol.ConnectionID, s packetHandler) { + Expect(s).To(BeAssignableToTypeOf(&closedLocalSession{})) Expect(s.Close()).To(Succeed()) Eventually(areClosedSessionsRunning).Should(BeFalse()) }) @@ -350,7 +351,9 @@ var _ = Describe("Session", func() { It("handles CONNECTION_CLOSE frames, with a transport error code", func() { testErr := qerr.Error(qerr.StreamLimitError, "foobar") streamManager.EXPECT().CloseWithError(testErr) - sessionRunner.EXPECT().Remove(gomock.Any()) + sessionRunner.EXPECT().ReplaceWithClosed(sess.srcConnID, gomock.Any()).Do(func(_ protocol.ConnectionID, s packetHandler) { + Expect(s).To(BeAssignableToTypeOf(&closedRemoteSession{})) + }) cryptoSetup.EXPECT().Close() go func() { @@ -369,7 +372,9 @@ var _ = Describe("Session", func() { It("handles CONNECTION_CLOSE frames, with an application error code", func() { testErr := qerr.ApplicationError(0x1337, "foobar") streamManager.EXPECT().CloseWithError(testErr) - sessionRunner.EXPECT().Remove(gomock.Any()) + sessionRunner.EXPECT().ReplaceWithClosed(sess.srcConnID, gomock.Any()).Do(func(_ protocol.ConnectionID, s packetHandler) { + Expect(s).To(BeAssignableToTypeOf(&closedRemoteSession{})) + }) cryptoSetup.EXPECT().Close() go func() { @@ -1764,7 +1769,7 @@ var _ = Describe("Client Session", func() { // Illustrates that an injected Initial with a CONNECTION_CLOSE frame causes // the connection to immediately break down It("fails on Initial-level CONNECTION_CLOSE frame", func() { - sessionRunner.EXPECT().Remove(gomock.Any()) + sessionRunner.EXPECT().ReplaceWithClosed(gomock.Any(), gomock.Any()) connCloseFrame := testutils.ComposeConnCloseFrame() initialPacket := testutils.ComposeInitialPacket(sess.destConnID, sess.srcConnID, sess.version, sess.destConnID, []wire.Frame{connCloseFrame}) Expect(sess.handlePacketImpl(wrapPacket(initialPacket))).To(BeTrue())