From 2ea6a294a9ec3800e04bd01bb86a64b37f6af32e Mon Sep 17 00:00:00 2001 From: Marten Seemann Date: Sat, 22 Feb 2020 13:02:10 +0700 Subject: [PATCH] send the CONNECTION_CLOSE in all available encryption levels --- internal/handshake/crypto_setup.go | 2 +- mock_packer_test.go | 6 +- packet_packer.go | 105 +++++++++++++------ packet_packer_test.go | 162 ++++++++++++++++++++++++++--- session.go | 18 +--- session_test.go | 97 +++++++---------- 6 files changed, 268 insertions(+), 122 deletions(-) diff --git a/internal/handshake/crypto_setup.go b/internal/handshake/crypto_setup.go index 6fc0a83b..e22df8c8 100644 --- a/internal/handshake/crypto_setup.go +++ b/internal/handshake/crypto_setup.go @@ -700,7 +700,7 @@ func (h *cryptoSetup) Get0RTTSealer() (LongHeaderSealer, error) { defer h.mutex.Unlock() if h.zeroRTTSealer == nil { - return nil, errors.New("CryptoSetup: 0-RTT sealer not available") + return nil, ErrKeysDropped } return h.zeroRTTSealer, nil } diff --git a/mock_packer_test.go b/mock_packer_test.go index 2349880f..a14d282c 100644 --- a/mock_packer_test.go +++ b/mock_packer_test.go @@ -10,7 +10,7 @@ import ( gomock "github.com/golang/mock/gomock" handshake "github.com/lucas-clemente/quic-go/internal/handshake" protocol "github.com/lucas-clemente/quic-go/internal/protocol" - wire "github.com/lucas-clemente/quic-go/internal/wire" + qerr "github.com/lucas-clemente/quic-go/internal/qerr" ) // MockPacker is a mock of Packer interface @@ -94,10 +94,10 @@ func (mr *MockPackerMockRecorder) PackCoalescedPacket() *gomock.Call { } // PackConnectionClose mocks base method -func (m *MockPacker) PackConnectionClose(arg0 *wire.ConnectionCloseFrame) (*packedPacket, error) { +func (m *MockPacker) PackConnectionClose(arg0 *qerr.QuicError) (*coalescedPacket, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "PackConnectionClose", arg0) - ret0, _ := ret[0].(*packedPacket) + ret0, _ := ret[0].(*coalescedPacket) ret1, _ := ret[1].(error) return ret0, ret1 } diff --git a/packet_packer.go b/packet_packer.go index b52b43e0..f61933bb 100644 --- a/packet_packer.go +++ b/packet_packer.go @@ -7,6 +7,8 @@ import ( "net" "time" + "github.com/lucas-clemente/quic-go/internal/qerr" + "github.com/lucas-clemente/quic-go/internal/ackhandler" "github.com/lucas-clemente/quic-go/internal/handshake" "github.com/lucas-clemente/quic-go/internal/protocol" @@ -19,7 +21,7 @@ type packer interface { PackPacket() (*packedPacket, error) MaybePackProbePacket(protocol.EncryptionLevel) (*packedPacket, error) MaybePackAckPacket(handshakeConfirmed bool) (*packedPacket, error) - PackConnectionClose(*wire.ConnectionCloseFrame) (*packedPacket, error) + PackConnectionClose(*qerr.QuicError) (*coalescedPacket, error) HandleTransportParameters(*handshake.TransportParameters) SetToken([]byte) @@ -196,36 +198,81 @@ func newPacketPacker( } // PackConnectionClose packs a packet that ONLY contains a ConnectionCloseFrame -func (p *packetPacker) PackConnectionClose(ccf *wire.ConnectionCloseFrame) (*packedPacket, error) { - payload := payload{ - frames: []ackhandler.Frame{{Frame: ccf}}, - length: ccf.Length(p.version), - } - // send the CONNECTION_CLOSE frame with the highest available encryption level - var err error - var hdr *wire.ExtendedHeader - var sealer sealer - encLevel := protocol.Encryption1RTT - s, err := p.cryptoSetup.Get1RTTSealer() - if err != nil { - encLevel = protocol.EncryptionHandshake - sealer, err = p.cryptoSetup.GetHandshakeSealer() - if err != nil { - encLevel = protocol.EncryptionInitial - sealer, err = p.cryptoSetup.GetInitialSealer() - if err != nil { - return nil, err - } - hdr = p.getLongHeader(protocol.EncryptionInitial) - } else { - hdr = p.getLongHeader(protocol.EncryptionHandshake) - } - } else { - sealer = s - hdr = p.getShortHeader(s.KeyPhase()) +func (p *packetPacker) PackConnectionClose(quicErr *qerr.QuicError) (*coalescedPacket, error) { + var reason string + // don't send details of crypto errors + if !quicErr.IsCryptoError() { + reason = quicErr.ErrorMessage } - return p.writeSinglePacket(hdr, payload, encLevel, sealer) + buffer := getPacketBuffer() + contents := make([]*packetContents, 0, 1) + for _, encLevel := range []protocol.EncryptionLevel{protocol.EncryptionInitial, protocol.EncryptionHandshake, protocol.Encryption0RTT, protocol.Encryption1RTT} { + if p.perspective == protocol.PerspectiveServer && encLevel == protocol.Encryption0RTT { + continue + } + quicErrToSend := quicErr + reasonPhrase := reason + if encLevel == protocol.EncryptionInitial || encLevel == protocol.EncryptionHandshake { + // don't send application errors in Initial or Handshake packets + if quicErr.IsApplicationError() { + quicErrToSend = qerr.UserCanceledError + reasonPhrase = "" + } + } + ccf := &wire.ConnectionCloseFrame{ + IsApplicationError: quicErrToSend.IsApplicationError(), + ErrorCode: quicErrToSend.ErrorCode, + FrameType: quicErrToSend.FrameType, + ReasonPhrase: reasonPhrase, + } + payload := payload{ + frames: []ackhandler.Frame{{Frame: ccf}}, + length: ccf.Length(p.version), + } + + var sealer sealer + var err error + var keyPhase protocol.KeyPhaseBit // only set for 1-RTT + switch encLevel { + case protocol.EncryptionInitial: + sealer, err = p.cryptoSetup.GetInitialSealer() + case protocol.EncryptionHandshake: + sealer, err = p.cryptoSetup.GetHandshakeSealer() + case protocol.Encryption0RTT: + sealer, err = p.cryptoSetup.Get0RTTSealer() + case protocol.Encryption1RTT: + var s handshake.ShortHeaderSealer + s, err = p.cryptoSetup.Get1RTTSealer() + if err == nil { + keyPhase = s.KeyPhase() + } + sealer = s + } + if err == handshake.ErrKeysNotYetAvailable || err == handshake.ErrKeysDropped { + continue + } + if err != nil { + return nil, err + } + var hdr *wire.ExtendedHeader + if encLevel == protocol.Encryption1RTT { + hdr = p.getShortHeader(keyPhase) + } else { + hdr = p.getLongHeader(encLevel) + } + c, err := p.appendPacket(buffer, hdr, payload, encLevel, sealer) + if err != nil { + return nil, err + } + contents = append(contents, c) + } + + if p.perspective == protocol.PerspectiveClient && contents[0].header.Type == protocol.PacketTypeInitial { + p.padPacket(buffer) + } + + return &coalescedPacket{buffer: buffer, packets: contents}, nil } func (p *packetPacker) MaybePackAckPacket(handshakeConfirmed bool) (*packedPacket, error) { diff --git a/packet_packer_test.go b/packet_packer_test.go index b7124d5a..79ce793c 100644 --- a/packet_packer_test.go +++ b/packet_packer_test.go @@ -6,6 +6,8 @@ import ( "net" "time" + "github.com/lucas-clemente/quic-go/internal/qerr" + "github.com/lucas-clemente/quic-go/internal/ackhandler" "github.com/golang/mock/gomock" @@ -275,6 +277,151 @@ var _ = Describe("Packet packer", func() { }) }) + Context("packing CONNECTION_CLOSE", func() { + It("clears the reason phrase for crypto errors", func() { + pnManager.EXPECT().PeekPacketNumber(protocol.EncryptionHandshake).Return(protocol.PacketNumber(0x42), protocol.PacketNumberLen2) + pnManager.EXPECT().PopPacketNumber(protocol.EncryptionHandshake).Return(protocol.PacketNumber(0x42)) + sealingManager.EXPECT().GetInitialSealer().Return(nil, handshake.ErrKeysDropped) + sealingManager.EXPECT().GetHandshakeSealer().Return(getSealer(), nil) + sealingManager.EXPECT().Get1RTTSealer().Return(nil, handshake.ErrKeysNotYetAvailable) + quicErr := qerr.CryptoError(0x42, "crypto error") + quicErr.FrameType = 0x1234 + p, err := packer.PackConnectionClose(quicErr) + Expect(err).ToNot(HaveOccurred()) + Expect(p.packets).To(HaveLen(1)) + Expect(p.packets[0].header.Type).To(Equal(protocol.PacketTypeHandshake)) + Expect(p.packets[0].frames).To(HaveLen(1)) + Expect(p.packets[0].frames[0].Frame).To(BeAssignableToTypeOf(&wire.ConnectionCloseFrame{})) + ccf := p.packets[0].frames[0].Frame.(*wire.ConnectionCloseFrame) + Expect(ccf.IsApplicationError).To(BeFalse()) + Expect(ccf.ErrorCode).To(BeEquivalentTo(0x100 + 0x42)) + Expect(ccf.FrameType).To(BeEquivalentTo(0x1234)) + Expect(ccf.ReasonPhrase).To(BeEmpty()) + }) + + It("packs a CONNECTION_CLOSE in 1-RTT", func() { + pnManager.EXPECT().PeekPacketNumber(protocol.Encryption1RTT).Return(protocol.PacketNumber(0x42), protocol.PacketNumberLen2) + pnManager.EXPECT().PopPacketNumber(protocol.Encryption1RTT).Return(protocol.PacketNumber(0x42)) + sealingManager.EXPECT().GetInitialSealer().Return(nil, handshake.ErrKeysDropped) + sealingManager.EXPECT().GetHandshakeSealer().Return(nil, handshake.ErrKeysDropped) + sealingManager.EXPECT().Get1RTTSealer().Return(getSealer(), nil) + // expect no framer.PopStreamFrames + p, err := packer.PackConnectionClose(qerr.Error(qerr.CryptoBufferExceeded, "test error")) + Expect(err).ToNot(HaveOccurred()) + Expect(p.packets).To(HaveLen(1)) + Expect(p.packets[0].header.IsLongHeader).To(BeFalse()) + Expect(p.packets[0].frames).To(HaveLen(1)) + Expect(p.packets[0].frames[0].Frame).To(BeAssignableToTypeOf(&wire.ConnectionCloseFrame{})) + ccf := p.packets[0].frames[0].Frame.(*wire.ConnectionCloseFrame) + Expect(ccf.IsApplicationError).To(BeFalse()) + Expect(ccf.ErrorCode).To(Equal(qerr.CryptoBufferExceeded)) + Expect(ccf.ReasonPhrase).To(Equal("test error")) + }) + + It("packs a CONNECTION_CLOSE in all available encryption levels, and replaces application errors in Initial and Handshake", func() { + pnManager.EXPECT().PeekPacketNumber(protocol.EncryptionInitial).Return(protocol.PacketNumber(1), protocol.PacketNumberLen2) + pnManager.EXPECT().PopPacketNumber(protocol.EncryptionInitial).Return(protocol.PacketNumber(1)) + pnManager.EXPECT().PeekPacketNumber(protocol.EncryptionHandshake).Return(protocol.PacketNumber(2), protocol.PacketNumberLen2) + pnManager.EXPECT().PopPacketNumber(protocol.EncryptionHandshake).Return(protocol.PacketNumber(2)) + pnManager.EXPECT().PeekPacketNumber(protocol.Encryption1RTT).Return(protocol.PacketNumber(3), protocol.PacketNumberLen2) + pnManager.EXPECT().PopPacketNumber(protocol.Encryption1RTT).Return(protocol.PacketNumber(3)) + sealingManager.EXPECT().GetInitialSealer().Return(getSealer(), nil) + sealingManager.EXPECT().GetHandshakeSealer().Return(getSealer(), nil) + sealingManager.EXPECT().Get1RTTSealer().Return(getSealer(), nil) + p, err := packer.PackConnectionClose(qerr.ApplicationError(0x1337, "test error")) + Expect(err).ToNot(HaveOccurred()) + Expect(p.packets).To(HaveLen(3)) + Expect(p.packets[0].header.Type).To(Equal(protocol.PacketTypeInitial)) + Expect(p.packets[0].header.PacketNumber).To(Equal(protocol.PacketNumber(1))) + Expect(p.packets[0].frames).To(HaveLen(1)) + Expect(p.packets[0].frames[0].Frame).To(BeAssignableToTypeOf(&wire.ConnectionCloseFrame{})) + ccf := p.packets[0].frames[0].Frame.(*wire.ConnectionCloseFrame) + Expect(ccf.IsApplicationError).To(BeFalse()) + Expect(ccf.ErrorCode).To(Equal(qerr.UserCanceledError.ErrorCode)) + Expect(ccf.ReasonPhrase).To(BeEmpty()) + Expect(p.packets[1].header.Type).To(Equal(protocol.PacketTypeHandshake)) + Expect(p.packets[1].header.PacketNumber).To(Equal(protocol.PacketNumber(2))) + Expect(p.packets[1].frames).To(HaveLen(1)) + Expect(p.packets[1].frames[0].Frame).To(BeAssignableToTypeOf(&wire.ConnectionCloseFrame{})) + ccf = p.packets[1].frames[0].Frame.(*wire.ConnectionCloseFrame) + Expect(ccf.IsApplicationError).To(BeFalse()) + Expect(ccf.ErrorCode).To(Equal(qerr.UserCanceledError.ErrorCode)) + Expect(ccf.ReasonPhrase).To(BeEmpty()) + Expect(p.packets[2].header.IsLongHeader).To(BeFalse()) + Expect(p.packets[2].header.PacketNumber).To(Equal(protocol.PacketNumber(3))) + Expect(p.packets[2].frames).To(HaveLen(1)) + Expect(p.packets[2].frames[0].Frame).To(BeAssignableToTypeOf(&wire.ConnectionCloseFrame{})) + ccf = p.packets[2].frames[0].Frame.(*wire.ConnectionCloseFrame) + Expect(ccf.IsApplicationError).To(BeTrue()) + Expect(ccf.ErrorCode).To(BeEquivalentTo(0x1337)) + Expect(ccf.ReasonPhrase).To(Equal("test error")) + }) + + It("packs a CONNECTION_CLOSE in all available encryption levels, as a client", func() { + packer.perspective = protocol.PerspectiveClient + pnManager.EXPECT().PeekPacketNumber(protocol.EncryptionHandshake).Return(protocol.PacketNumber(1), protocol.PacketNumberLen2) + pnManager.EXPECT().PopPacketNumber(protocol.EncryptionHandshake).Return(protocol.PacketNumber(1)) + pnManager.EXPECT().PeekPacketNumber(protocol.Encryption1RTT).Return(protocol.PacketNumber(2), protocol.PacketNumberLen2) + pnManager.EXPECT().PopPacketNumber(protocol.Encryption1RTT).Return(protocol.PacketNumber(2)) + sealingManager.EXPECT().GetInitialSealer().Return(nil, handshake.ErrKeysDropped) + sealingManager.EXPECT().GetHandshakeSealer().Return(getSealer(), nil) + sealingManager.EXPECT().Get0RTTSealer().Return(nil, handshake.ErrKeysDropped) + sealingManager.EXPECT().Get1RTTSealer().Return(getSealer(), nil) + p, err := packer.PackConnectionClose(qerr.ApplicationError(0x1337, "test error")) + Expect(err).ToNot(HaveOccurred()) + Expect(p.packets).To(HaveLen(2)) + Expect(p.buffer.Len()).To(BeNumerically("<", protocol.MinInitialPacketSize)) + Expect(p.packets[0].header.Type).To(Equal(protocol.PacketTypeHandshake)) + Expect(p.packets[0].header.PacketNumber).To(Equal(protocol.PacketNumber(1))) + Expect(p.packets[0].frames).To(HaveLen(1)) + Expect(p.packets[0].frames[0].Frame).To(BeAssignableToTypeOf(&wire.ConnectionCloseFrame{})) + ccf := p.packets[0].frames[0].Frame.(*wire.ConnectionCloseFrame) + Expect(ccf.IsApplicationError).To(BeFalse()) + Expect(ccf.ErrorCode).To(Equal(qerr.UserCanceledError.ErrorCode)) + Expect(ccf.ReasonPhrase).To(BeEmpty()) + Expect(p.packets[1].header.IsLongHeader).To(BeFalse()) + Expect(p.packets[1].header.PacketNumber).To(Equal(protocol.PacketNumber(2))) + Expect(p.packets[1].frames).To(HaveLen(1)) + Expect(p.packets[1].frames[0].Frame).To(BeAssignableToTypeOf(&wire.ConnectionCloseFrame{})) + ccf = p.packets[1].frames[0].Frame.(*wire.ConnectionCloseFrame) + Expect(ccf.IsApplicationError).To(BeTrue()) + Expect(ccf.ErrorCode).To(BeEquivalentTo(0x1337)) + Expect(ccf.ReasonPhrase).To(Equal("test error")) + }) + + It("packs a CONNECTION_CLOSE in all available encryption levels and pads, as a client", func() { + packer.perspective = protocol.PerspectiveClient + pnManager.EXPECT().PeekPacketNumber(protocol.EncryptionInitial).Return(protocol.PacketNumber(1), protocol.PacketNumberLen2) + pnManager.EXPECT().PopPacketNumber(protocol.EncryptionInitial).Return(protocol.PacketNumber(1)) + pnManager.EXPECT().PeekPacketNumber(protocol.Encryption0RTT).Return(protocol.PacketNumber(2), protocol.PacketNumberLen2) + pnManager.EXPECT().PopPacketNumber(protocol.Encryption0RTT).Return(protocol.PacketNumber(2)) + sealingManager.EXPECT().GetInitialSealer().Return(getSealer(), nil) + sealingManager.EXPECT().GetHandshakeSealer().Return(nil, handshake.ErrKeysNotYetAvailable) + sealingManager.EXPECT().Get0RTTSealer().Return(getSealer(), nil) + sealingManager.EXPECT().Get1RTTSealer().Return(nil, handshake.ErrKeysNotYetAvailable) + p, err := packer.PackConnectionClose(qerr.ApplicationError(0x1337, "test error")) + Expect(err).ToNot(HaveOccurred()) + Expect(p.packets).To(HaveLen(2)) + Expect(p.buffer.Len()).To(BeEquivalentTo(protocol.MinInitialPacketSize)) + Expect(p.packets[0].header.Type).To(Equal(protocol.PacketTypeInitial)) + Expect(p.packets[0].header.PacketNumber).To(Equal(protocol.PacketNumber(1))) + Expect(p.packets[0].frames).To(HaveLen(1)) + Expect(p.packets[0].frames[0].Frame).To(BeAssignableToTypeOf(&wire.ConnectionCloseFrame{})) + ccf := p.packets[0].frames[0].Frame.(*wire.ConnectionCloseFrame) + Expect(ccf.IsApplicationError).To(BeFalse()) + Expect(ccf.ErrorCode).To(Equal(qerr.UserCanceledError.ErrorCode)) + Expect(ccf.ReasonPhrase).To(BeEmpty()) + Expect(p.packets[1].header.Type).To(Equal(protocol.PacketType0RTT)) + Expect(p.packets[1].header.PacketNumber).To(Equal(protocol.PacketNumber(2))) + Expect(p.packets[1].frames).To(HaveLen(1)) + Expect(p.packets[1].frames[0].Frame).To(BeAssignableToTypeOf(&wire.ConnectionCloseFrame{})) + ccf = p.packets[1].frames[0].Frame.(*wire.ConnectionCloseFrame) + Expect(ccf.IsApplicationError).To(BeTrue()) + Expect(ccf.ErrorCode).To(BeEquivalentTo(0x1337)) + Expect(ccf.ReasonPhrase).To(Equal("test error")) + }) + }) + Context("packing normal packets", func() { BeforeEach(func() { initialStream.EXPECT().HasData().AnyTimes() @@ -344,21 +491,6 @@ var _ = Describe("Packet packer", func() { Expect(p.ack).To(Equal(ack)) }) - It("packs a CONNECTION_CLOSE", func() { - pnManager.EXPECT().PeekPacketNumber(protocol.Encryption1RTT).Return(protocol.PacketNumber(0x42), protocol.PacketNumberLen2) - pnManager.EXPECT().PopPacketNumber(protocol.Encryption1RTT).Return(protocol.PacketNumber(0x42)) - // expect no framer.PopStreamFrames - ccf := wire.ConnectionCloseFrame{ - ErrorCode: 0x1337, - ReasonPhrase: "foobar", - } - sealingManager.EXPECT().Get1RTTSealer().Return(getSealer(), nil) - p, err := packer.PackConnectionClose(&ccf) - Expect(err).ToNot(HaveOccurred()) - Expect(p.frames).To(HaveLen(1)) - Expect(p.frames[0].Frame).To(Equal(&ccf)) - }) - It("packs control frames", func() { pnManager.EXPECT().PeekPacketNumber(protocol.Encryption1RTT).Return(protocol.PacketNumber(0x42), protocol.PacketNumberLen2) pnManager.EXPECT().PopPacketNumber(protocol.Encryption1RTT).Return(protocol.PacketNumber(0x42)) diff --git a/session.go b/session.go index 5f57d70b..933fc11d 100644 --- a/session.go +++ b/session.go @@ -1360,25 +1360,11 @@ func (s *session) sendPackedPacket(packet *packedPacket) { } func (s *session) sendConnectionClose(quicErr *qerr.QuicError) ([]byte, error) { - // don't send application errors in Initial or Handshake packets - if quicErr.IsApplicationError() && !s.handshakeComplete { - quicErr = qerr.UserCanceledError - } - var reason string - // don't send details of crypto errors - if !quicErr.IsCryptoError() { - reason = quicErr.ErrorMessage - } - packet, err := s.packer.PackConnectionClose(&wire.ConnectionCloseFrame{ - IsApplicationError: quicErr.IsApplicationError(), - ErrorCode: quicErr.ErrorCode, - FrameType: quicErr.FrameType, - ReasonPhrase: reason, - }) + packet, err := s.packer.PackConnectionClose(quicErr) if err != nil { return nil, err } - s.logPacket(time.Now(), packet) + s.logCoalescedPacket(time.Now(), packet) return packet.buffer.Data, s.conn.Write(packet.buffer.Data) } diff --git a/session_test.go b/session_test.go index 463e12b9..abc25a33 100644 --- a/session_test.go +++ b/session_test.go @@ -406,12 +406,10 @@ var _ = Describe("Session", func() { cryptoSetup.EXPECT().Close() buffer := getPacketBuffer() buffer.Data = append(buffer.Data, []byte("connection close")...) - packer.EXPECT().PackConnectionClose(gomock.Any()).DoAndReturn(func(f *wire.ConnectionCloseFrame) (*packedPacket, error) { - Expect(f.IsApplicationError).To(BeTrue()) - Expect(f.ErrorCode).To(Equal(qerr.NoError)) - Expect(f.FrameType).To(BeZero()) - Expect(f.ReasonPhrase).To(BeEmpty()) - return &packedPacket{buffer: buffer}, nil + packer.EXPECT().PackConnectionClose(gomock.Any()).DoAndReturn(func(quicErr *qerr.QuicError) (*coalescedPacket, error) { + Expect(quicErr.ErrorCode).To(BeEquivalentTo(qerr.NoError)) + Expect(quicErr.ErrorMessage).To(BeEmpty()) + return &coalescedPacket{buffer: buffer}, nil }) mconn.EXPECT().Write([]byte("connection close")) sess.shutdown() @@ -423,7 +421,7 @@ var _ = Describe("Session", func() { streamManager.EXPECT().CloseWithError(gomock.Any()) expectReplaceWithClosed() cryptoSetup.EXPECT().Close() - packer.EXPECT().PackConnectionClose(gomock.Any()).Return(&packedPacket{buffer: getPacketBuffer()}, nil) + packer.EXPECT().PackConnectionClose(gomock.Any()).Return(&coalescedPacket{buffer: getPacketBuffer()}, nil) mconn.EXPECT().Write(gomock.Any()) sess.shutdown() sess.shutdown() @@ -435,11 +433,11 @@ var _ = Describe("Session", func() { streamManager.EXPECT().CloseWithError(qerr.ApplicationError(0x1337, "test error")) expectReplaceWithClosed() cryptoSetup.EXPECT().Close() - packer.EXPECT().PackConnectionClose(gomock.Any()).DoAndReturn(func(f *wire.ConnectionCloseFrame) (*packedPacket, error) { - Expect(f.IsApplicationError).To(BeTrue()) - Expect(f.ErrorCode).To(BeEquivalentTo(0x1337)) - Expect(f.ReasonPhrase).To(Equal("test error")) - return &packedPacket{buffer: getPacketBuffer()}, nil + packer.EXPECT().PackConnectionClose(gomock.Any()).DoAndReturn(func(quicErr *qerr.QuicError) (*coalescedPacket, error) { + Expect(quicErr.IsApplicationError()).To(BeTrue()) + Expect(quicErr.ErrorCode).To(BeEquivalentTo(0x1337)) + Expect(quicErr.ErrorMessage).To(Equal("test error")) + return &coalescedPacket{buffer: getPacketBuffer()}, nil }) mconn.EXPECT().Write(gomock.Any()) sess.CloseWithError(0x1337, "test error") @@ -452,12 +450,12 @@ var _ = Describe("Session", func() { streamManager.EXPECT().CloseWithError(testErr) expectReplaceWithClosed() cryptoSetup.EXPECT().Close() - packer.EXPECT().PackConnectionClose(gomock.Any()).DoAndReturn(func(f *wire.ConnectionCloseFrame) (*packedPacket, error) { - Expect(f.IsApplicationError).To(BeFalse()) - Expect(f.FrameType).To(BeEquivalentTo(0x42)) - Expect(f.ErrorCode).To(BeEquivalentTo(0x1337)) - Expect(f.ReasonPhrase).To(Equal("test error")) - return &packedPacket{buffer: getPacketBuffer()}, nil + packer.EXPECT().PackConnectionClose(gomock.Any()).DoAndReturn(func(quicErr *qerr.QuicError) (*coalescedPacket, error) { + Expect(quicErr.IsApplicationError()).To(BeFalse()) + Expect(quicErr.FrameType).To(BeEquivalentTo(0x42)) + Expect(quicErr.ErrorCode).To(BeEquivalentTo(0x1337)) + Expect(quicErr.ErrorMessage).To(Equal("test error")) + return &coalescedPacket{buffer: getPacketBuffer()}, nil }) mconn.EXPECT().Write(gomock.Any()) sess.closeLocal(testErr) @@ -465,23 +463,6 @@ var _ = Describe("Session", func() { Expect(sess.Context().Done()).To(BeClosed()) }) - It("doesn't send application-level error before the handshake completes", func() { - sess.handshakeComplete = false - streamManager.EXPECT().CloseWithError(qerr.ApplicationError(0x1337, "test error")) - expectReplaceWithClosed() - cryptoSetup.EXPECT().Close() - packer.EXPECT().PackConnectionClose(gomock.Any()).DoAndReturn(func(f *wire.ConnectionCloseFrame) (*packedPacket, error) { - Expect(f.IsApplicationError).To(BeFalse()) - Expect(f.ErrorCode).To(BeEquivalentTo(0x15a)) - Expect(f.ReasonPhrase).To(BeEmpty()) - return &packedPacket{buffer: getPacketBuffer()}, nil - }) - mconn.EXPECT().Write(gomock.Any()) - sess.CloseWithError(0x1337, "test error") - Eventually(areSessionsRunning).Should(BeFalse()) - Expect(sess.Context().Done()).To(BeClosed()) - }) - It("closes the session in order to recreate it", func() { streamManager.EXPECT().CloseWithError(gomock.Any()) sessionRunner.EXPECT().Remove(gomock.Any()).AnyTimes() @@ -507,7 +488,7 @@ var _ = Describe("Session", func() { streamManager.EXPECT().CloseWithError(gomock.Any()) expectReplaceWithClosed() cryptoSetup.EXPECT().Close() - packer.EXPECT().PackConnectionClose(gomock.Any()).Return(&packedPacket{buffer: getPacketBuffer()}, nil) + packer.EXPECT().PackConnectionClose(gomock.Any()).Return(&coalescedPacket{buffer: getPacketBuffer()}, nil) returned := make(chan struct{}) go func() { defer GinkgoRecover() @@ -596,7 +577,7 @@ var _ = Describe("Session", func() { unpacker.EXPECT().Unpack(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, handshake.ErrDecryptionFailed) streamManager.EXPECT().CloseWithError(gomock.Any()) cryptoSetup.EXPECT().Close() - packer.EXPECT().PackConnectionClose(gomock.Any()).Return(&packedPacket{buffer: getPacketBuffer()}, nil) + packer.EXPECT().PackConnectionClose(gomock.Any()).Return(&coalescedPacket{buffer: getPacketBuffer()}, nil) go func() { defer GinkgoRecover() cryptoSetup.EXPECT().RunHandshake().MaxTimes(1) @@ -618,7 +599,7 @@ var _ = Describe("Session", func() { unpacker.EXPECT().Unpack(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, wire.ErrInvalidReservedBits) streamManager.EXPECT().CloseWithError(gomock.Any()) cryptoSetup.EXPECT().Close() - packer.EXPECT().PackConnectionClose(gomock.Any()).Return(&packedPacket{buffer: getPacketBuffer()}, nil) + packer.EXPECT().PackConnectionClose(gomock.Any()).Return(&coalescedPacket{buffer: getPacketBuffer()}, nil) done := make(chan struct{}) go func() { defer GinkgoRecover() @@ -642,7 +623,7 @@ var _ = Describe("Session", func() { unpacker.EXPECT().Unpack(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, testErr) streamManager.EXPECT().CloseWithError(gomock.Any()) cryptoSetup.EXPECT().Close() - packer.EXPECT().PackConnectionClose(gomock.Any()).Return(&packedPacket{buffer: getPacketBuffer()}, nil) + packer.EXPECT().PackConnectionClose(gomock.Any()).Return(&coalescedPacket{buffer: getPacketBuffer()}, nil) runErr := make(chan error) go func() { defer GinkgoRecover() @@ -668,7 +649,7 @@ var _ = Describe("Session", func() { }, nil) streamManager.EXPECT().CloseWithError(gomock.Any()) cryptoSetup.EXPECT().Close() - packer.EXPECT().PackConnectionClose(gomock.Any()).Return(&packedPacket{buffer: getPacketBuffer()}, nil) + packer.EXPECT().PackConnectionClose(gomock.Any()).Return(&coalescedPacket{buffer: getPacketBuffer()}, nil) done := make(chan struct{}) go func() { defer GinkgoRecover() @@ -864,7 +845,7 @@ var _ = Describe("Session", func() { AfterEach(func() { streamManager.EXPECT().CloseWithError(gomock.Any()) - packer.EXPECT().PackConnectionClose(gomock.Any()).Return(&packedPacket{buffer: getPacketBuffer()}, nil) + packer.EXPECT().PackConnectionClose(gomock.Any()).Return(&coalescedPacket{buffer: getPacketBuffer()}, nil) expectReplaceWithClosed() cryptoSetup.EXPECT().Close() mconn.EXPECT().Write(gomock.Any()) @@ -995,7 +976,7 @@ var _ = Describe("Session", func() { AfterEach(func() { // make the go routine return - packer.EXPECT().PackConnectionClose(gomock.Any()).Return(&packedPacket{buffer: getPacketBuffer()}, nil) + packer.EXPECT().PackConnectionClose(gomock.Any()).Return(&coalescedPacket{buffer: getPacketBuffer()}, nil) expectReplaceWithClosed() cryptoSetup.EXPECT().Close() mconn.EXPECT().Write(gomock.Any()) @@ -1114,7 +1095,7 @@ var _ = Describe("Session", func() { // make the go routine return expectReplaceWithClosed() streamManager.EXPECT().CloseWithError(gomock.Any()) - packer.EXPECT().PackConnectionClose(gomock.Any()).Return(&packedPacket{buffer: getPacketBuffer()}, nil) + packer.EXPECT().PackConnectionClose(gomock.Any()).Return(&coalescedPacket{buffer: getPacketBuffer()}, nil) cryptoSetup.EXPECT().Close() mconn.EXPECT().Write(gomock.Any()) sess.shutdown() @@ -1236,7 +1217,7 @@ var _ = Describe("Session", func() { // make sure the go routine returns streamManager.EXPECT().CloseWithError(gomock.Any()) expectReplaceWithClosed() - packer.EXPECT().PackConnectionClose(gomock.Any()).Return(&packedPacket{buffer: getPacketBuffer()}, nil) + packer.EXPECT().PackConnectionClose(gomock.Any()).Return(&coalescedPacket{buffer: getPacketBuffer()}, nil) cryptoSetup.EXPECT().Close() mconn.EXPECT().Write(gomock.Any()) sess.shutdown() @@ -1272,7 +1253,7 @@ var _ = Describe("Session", func() { // make sure the go routine returns streamManager.EXPECT().CloseWithError(gomock.Any()) expectReplaceWithClosed() - packer.EXPECT().PackConnectionClose(gomock.Any()).Return(&packedPacket{buffer: getPacketBuffer()}, nil) + packer.EXPECT().PackConnectionClose(gomock.Any()).Return(&coalescedPacket{buffer: getPacketBuffer()}, nil) cryptoSetup.EXPECT().Close() mconn.EXPECT().Write(gomock.Any()) sess.shutdown() @@ -1313,7 +1294,7 @@ var _ = Describe("Session", func() { // make sure the go routine returns streamManager.EXPECT().CloseWithError(gomock.Any()) expectReplaceWithClosed() - packer.EXPECT().PackConnectionClose(gomock.Any()).Return(&packedPacket{buffer: getPacketBuffer()}, nil) + packer.EXPECT().PackConnectionClose(gomock.Any()).Return(&coalescedPacket{buffer: getPacketBuffer()}, nil) cryptoSetup.EXPECT().Close() mconn.EXPECT().Write(gomock.Any()) sess.shutdown() @@ -1324,7 +1305,7 @@ var _ = Describe("Session", func() { packer.EXPECT().PackCoalescedPacket().AnyTimes() streamManager.EXPECT().CloseWithError(gomock.Any()) expectReplaceWithClosed() - packer.EXPECT().PackConnectionClose(gomock.Any()).Return(&packedPacket{buffer: getPacketBuffer()}, nil) + packer.EXPECT().PackConnectionClose(gomock.Any()).Return(&coalescedPacket{buffer: getPacketBuffer()}, nil) cryptoSetup.EXPECT().Close() go func() { defer GinkgoRecover() @@ -1369,7 +1350,7 @@ var _ = Describe("Session", func() { // make sure the go routine returns streamManager.EXPECT().CloseWithError(gomock.Any()) expectReplaceWithClosed() - packer.EXPECT().PackConnectionClose(gomock.Any()).Return(&packedPacket{buffer: getPacketBuffer()}, nil) + packer.EXPECT().PackConnectionClose(gomock.Any()).Return(&coalescedPacket{buffer: getPacketBuffer()}, nil) cryptoSetup.EXPECT().Close() sess.shutdown() Eventually(sess.Context().Done()).Should(BeClosed()) @@ -1385,7 +1366,7 @@ var _ = Describe("Session", func() { }() streamManager.EXPECT().CloseWithError(gomock.Any()) expectReplaceWithClosed() - packer.EXPECT().PackConnectionClose(gomock.Any()).Return(&packedPacket{buffer: getPacketBuffer()}, nil) + packer.EXPECT().PackConnectionClose(gomock.Any()).Return(&coalescedPacket{buffer: getPacketBuffer()}, nil) cryptoSetup.EXPECT().Close() mconn.EXPECT().Write(gomock.Any()) sess.shutdown() @@ -1404,7 +1385,7 @@ var _ = Describe("Session", func() { }() streamManager.EXPECT().CloseWithError(gomock.Any()) expectReplaceWithClosed() - packer.EXPECT().PackConnectionClose(gomock.Any()).Return(&packedPacket{buffer: getPacketBuffer()}, nil) + packer.EXPECT().PackConnectionClose(gomock.Any()).Return(&coalescedPacket{buffer: getPacketBuffer()}, nil) cryptoSetup.EXPECT().Close() mconn.EXPECT().Write(gomock.Any()) Expect(sess.CloseWithError(0x1337, testErr.Error())).To(Succeed()) @@ -1441,7 +1422,7 @@ var _ = Describe("Session", func() { Expect(s).To(BeAssignableToTypeOf(&closedLocalSession{})) s.shutdown() }).Times(4) // initial connection ID + initial client dest conn ID + 2 newly issued conn IDs - packer.EXPECT().PackConnectionClose(gomock.Any()).Return(&packedPacket{buffer: getPacketBuffer()}, nil) + packer.EXPECT().PackConnectionClose(gomock.Any()).Return(&coalescedPacket{buffer: getPacketBuffer()}, nil) cryptoSetup.EXPECT().Close() mconn.EXPECT().Write(gomock.Any()) sess.shutdown() @@ -1473,7 +1454,7 @@ var _ = Describe("Session", func() { // make the go routine return expectReplaceWithClosed() streamManager.EXPECT().CloseWithError(gomock.Any()) - packer.EXPECT().PackConnectionClose(gomock.Any()).Return(&packedPacket{buffer: getPacketBuffer()}, nil) + packer.EXPECT().PackConnectionClose(gomock.Any()).Return(&coalescedPacket{buffer: getPacketBuffer()}, nil) cryptoSetup.EXPECT().Close() mconn.EXPECT().Write(gomock.Any()) sess.shutdown() @@ -1571,9 +1552,9 @@ var _ = Describe("Session", func() { sess.handshakeComplete = false sess.config.MaxIdleTimeout = 9999 * time.Second sess.lastPacketReceivedTime = time.Now().Add(-time.Minute) - packer.EXPECT().PackConnectionClose(gomock.Any()).DoAndReturn(func(f *wire.ConnectionCloseFrame) (*packedPacket, error) { - Expect(f.ErrorCode).To(Equal(qerr.NoError)) - return &packedPacket{buffer: getPacketBuffer()}, nil + packer.EXPECT().PackConnectionClose(gomock.Any()).DoAndReturn(func(quicErr *qerr.QuicError) (*coalescedPacket, error) { + Expect(quicErr.ErrorCode).To(Equal(qerr.NoError)) + return &coalescedPacket{buffer: getPacketBuffer()}, nil }) // the handshake timeout is irrelevant here, since it depends on the time the session was created, // and not on the last network activity @@ -1629,7 +1610,7 @@ var _ = Describe("Session", func() { }() Consistently(sess.Context().Done()).ShouldNot(BeClosed()) // make the go routine return - packer.EXPECT().PackConnectionClose(gomock.Any()).Return(&packedPacket{buffer: getPacketBuffer()}, nil) + packer.EXPECT().PackConnectionClose(gomock.Any()).Return(&coalescedPacket{buffer: getPacketBuffer()}, nil) expectReplaceWithClosed() cryptoSetup.EXPECT().Close() mconn.EXPECT().Write(gomock.Any()) @@ -1805,7 +1786,7 @@ var _ = Describe("Client Session", func() { PacketNumberLen: protocol.PacketNumberLen2, }, []byte{0}))).To(BeTrue()) // make sure the go routine returns - packer.EXPECT().PackConnectionClose(gomock.Any()).Return(&packedPacket{buffer: getPacketBuffer()}, nil) + packer.EXPECT().PackConnectionClose(gomock.Any()).Return(&coalescedPacket{buffer: getPacketBuffer()}, nil) expectReplaceWithClosed() cryptoSetup.EXPECT().Close() mconn.EXPECT().Write(gomock.Any()) @@ -1930,7 +1911,7 @@ var _ = Describe("Client Session", func() { Expect(s).To(BeAssignableToTypeOf(&closedLocalSession{})) s.shutdown() }) - packer.EXPECT().PackConnectionClose(gomock.Any()).Return(&packedPacket{buffer: getPacketBuffer()}, nil).MaxTimes(1) + packer.EXPECT().PackConnectionClose(gomock.Any()).Return(&coalescedPacket{buffer: getPacketBuffer()}, nil).MaxTimes(1) cryptoSetup.EXPECT().Close() mconn.EXPECT().Write(gomock.Any()) }