send the CONNECTION_CLOSE in all available encryption levels

This commit is contained in:
Marten Seemann 2020-02-22 13:02:10 +07:00
parent 75f14a267e
commit 2ea6a294a9
6 changed files with 268 additions and 122 deletions

View file

@ -700,7 +700,7 @@ func (h *cryptoSetup) Get0RTTSealer() (LongHeaderSealer, error) {
defer h.mutex.Unlock() defer h.mutex.Unlock()
if h.zeroRTTSealer == nil { if h.zeroRTTSealer == nil {
return nil, errors.New("CryptoSetup: 0-RTT sealer not available") return nil, ErrKeysDropped
} }
return h.zeroRTTSealer, nil return h.zeroRTTSealer, nil
} }

View file

@ -10,7 +10,7 @@ import (
gomock "github.com/golang/mock/gomock" gomock "github.com/golang/mock/gomock"
handshake "github.com/lucas-clemente/quic-go/internal/handshake" handshake "github.com/lucas-clemente/quic-go/internal/handshake"
protocol "github.com/lucas-clemente/quic-go/internal/protocol" 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 // MockPacker is a mock of Packer interface
@ -94,10 +94,10 @@ func (mr *MockPackerMockRecorder) PackCoalescedPacket() *gomock.Call {
} }
// PackConnectionClose mocks base method // 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() m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "PackConnectionClose", arg0) ret := m.ctrl.Call(m, "PackConnectionClose", arg0)
ret0, _ := ret[0].(*packedPacket) ret0, _ := ret[0].(*coalescedPacket)
ret1, _ := ret[1].(error) ret1, _ := ret[1].(error)
return ret0, ret1 return ret0, ret1
} }

View file

@ -7,6 +7,8 @@ import (
"net" "net"
"time" "time"
"github.com/lucas-clemente/quic-go/internal/qerr"
"github.com/lucas-clemente/quic-go/internal/ackhandler" "github.com/lucas-clemente/quic-go/internal/ackhandler"
"github.com/lucas-clemente/quic-go/internal/handshake" "github.com/lucas-clemente/quic-go/internal/handshake"
"github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/internal/protocol"
@ -19,7 +21,7 @@ type packer interface {
PackPacket() (*packedPacket, error) PackPacket() (*packedPacket, error)
MaybePackProbePacket(protocol.EncryptionLevel) (*packedPacket, error) MaybePackProbePacket(protocol.EncryptionLevel) (*packedPacket, error)
MaybePackAckPacket(handshakeConfirmed bool) (*packedPacket, error) MaybePackAckPacket(handshakeConfirmed bool) (*packedPacket, error)
PackConnectionClose(*wire.ConnectionCloseFrame) (*packedPacket, error) PackConnectionClose(*qerr.QuicError) (*coalescedPacket, error)
HandleTransportParameters(*handshake.TransportParameters) HandleTransportParameters(*handshake.TransportParameters)
SetToken([]byte) SetToken([]byte)
@ -196,36 +198,81 @@ func newPacketPacker(
} }
// PackConnectionClose packs a packet that ONLY contains a ConnectionCloseFrame // PackConnectionClose packs a packet that ONLY contains a ConnectionCloseFrame
func (p *packetPacker) PackConnectionClose(ccf *wire.ConnectionCloseFrame) (*packedPacket, error) { 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
}
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{ payload := payload{
frames: []ackhandler.Frame{{Frame: ccf}}, frames: []ackhandler.Frame{{Frame: ccf}},
length: ccf.Length(p.version), 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 var sealer sealer
encLevel := protocol.Encryption1RTT var err error
s, err := p.cryptoSetup.Get1RTTSealer() var keyPhase protocol.KeyPhaseBit // only set for 1-RTT
if err != nil { switch encLevel {
encLevel = protocol.EncryptionHandshake case protocol.EncryptionInitial:
sealer, err = p.cryptoSetup.GetHandshakeSealer()
if err != nil {
encLevel = protocol.EncryptionInitial
sealer, err = p.cryptoSetup.GetInitialSealer() 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 { if err != nil {
return nil, err return nil, err
} }
hdr = p.getLongHeader(protocol.EncryptionInitial) var hdr *wire.ExtendedHeader
if encLevel == protocol.Encryption1RTT {
hdr = p.getShortHeader(keyPhase)
} else { } else {
hdr = p.getLongHeader(protocol.EncryptionHandshake) hdr = p.getLongHeader(encLevel)
} }
} else { c, err := p.appendPacket(buffer, hdr, payload, encLevel, sealer)
sealer = s if err != nil {
hdr = p.getShortHeader(s.KeyPhase()) return nil, err
}
contents = append(contents, c)
} }
return p.writeSinglePacket(hdr, payload, encLevel, sealer) 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) { func (p *packetPacker) MaybePackAckPacket(handshakeConfirmed bool) (*packedPacket, error) {

View file

@ -6,6 +6,8 @@ import (
"net" "net"
"time" "time"
"github.com/lucas-clemente/quic-go/internal/qerr"
"github.com/lucas-clemente/quic-go/internal/ackhandler" "github.com/lucas-clemente/quic-go/internal/ackhandler"
"github.com/golang/mock/gomock" "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() { Context("packing normal packets", func() {
BeforeEach(func() { BeforeEach(func() {
initialStream.EXPECT().HasData().AnyTimes() initialStream.EXPECT().HasData().AnyTimes()
@ -344,21 +491,6 @@ var _ = Describe("Packet packer", func() {
Expect(p.ack).To(Equal(ack)) 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() { It("packs control frames", func() {
pnManager.EXPECT().PeekPacketNumber(protocol.Encryption1RTT).Return(protocol.PacketNumber(0x42), protocol.PacketNumberLen2) pnManager.EXPECT().PeekPacketNumber(protocol.Encryption1RTT).Return(protocol.PacketNumber(0x42), protocol.PacketNumberLen2)
pnManager.EXPECT().PopPacketNumber(protocol.Encryption1RTT).Return(protocol.PacketNumber(0x42)) pnManager.EXPECT().PopPacketNumber(protocol.Encryption1RTT).Return(protocol.PacketNumber(0x42))

View file

@ -1360,25 +1360,11 @@ func (s *session) sendPackedPacket(packet *packedPacket) {
} }
func (s *session) sendConnectionClose(quicErr *qerr.QuicError) ([]byte, error) { func (s *session) sendConnectionClose(quicErr *qerr.QuicError) ([]byte, error) {
// don't send application errors in Initial or Handshake packets packet, err := s.packer.PackConnectionClose(quicErr)
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,
})
if err != nil { if err != nil {
return nil, err return nil, err
} }
s.logPacket(time.Now(), packet) s.logCoalescedPacket(time.Now(), packet)
return packet.buffer.Data, s.conn.Write(packet.buffer.Data) return packet.buffer.Data, s.conn.Write(packet.buffer.Data)
} }

View file

@ -406,12 +406,10 @@ var _ = Describe("Session", func() {
cryptoSetup.EXPECT().Close() cryptoSetup.EXPECT().Close()
buffer := getPacketBuffer() buffer := getPacketBuffer()
buffer.Data = append(buffer.Data, []byte("connection close")...) buffer.Data = append(buffer.Data, []byte("connection close")...)
packer.EXPECT().PackConnectionClose(gomock.Any()).DoAndReturn(func(f *wire.ConnectionCloseFrame) (*packedPacket, error) { packer.EXPECT().PackConnectionClose(gomock.Any()).DoAndReturn(func(quicErr *qerr.QuicError) (*coalescedPacket, error) {
Expect(f.IsApplicationError).To(BeTrue()) Expect(quicErr.ErrorCode).To(BeEquivalentTo(qerr.NoError))
Expect(f.ErrorCode).To(Equal(qerr.NoError)) Expect(quicErr.ErrorMessage).To(BeEmpty())
Expect(f.FrameType).To(BeZero()) return &coalescedPacket{buffer: buffer}, nil
Expect(f.ReasonPhrase).To(BeEmpty())
return &packedPacket{buffer: buffer}, nil
}) })
mconn.EXPECT().Write([]byte("connection close")) mconn.EXPECT().Write([]byte("connection close"))
sess.shutdown() sess.shutdown()
@ -423,7 +421,7 @@ var _ = Describe("Session", func() {
streamManager.EXPECT().CloseWithError(gomock.Any()) streamManager.EXPECT().CloseWithError(gomock.Any())
expectReplaceWithClosed() expectReplaceWithClosed()
cryptoSetup.EXPECT().Close() 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()) mconn.EXPECT().Write(gomock.Any())
sess.shutdown() sess.shutdown()
sess.shutdown() sess.shutdown()
@ -435,11 +433,11 @@ var _ = Describe("Session", func() {
streamManager.EXPECT().CloseWithError(qerr.ApplicationError(0x1337, "test error")) streamManager.EXPECT().CloseWithError(qerr.ApplicationError(0x1337, "test error"))
expectReplaceWithClosed() expectReplaceWithClosed()
cryptoSetup.EXPECT().Close() cryptoSetup.EXPECT().Close()
packer.EXPECT().PackConnectionClose(gomock.Any()).DoAndReturn(func(f *wire.ConnectionCloseFrame) (*packedPacket, error) { packer.EXPECT().PackConnectionClose(gomock.Any()).DoAndReturn(func(quicErr *qerr.QuicError) (*coalescedPacket, error) {
Expect(f.IsApplicationError).To(BeTrue()) Expect(quicErr.IsApplicationError()).To(BeTrue())
Expect(f.ErrorCode).To(BeEquivalentTo(0x1337)) Expect(quicErr.ErrorCode).To(BeEquivalentTo(0x1337))
Expect(f.ReasonPhrase).To(Equal("test error")) Expect(quicErr.ErrorMessage).To(Equal("test error"))
return &packedPacket{buffer: getPacketBuffer()}, nil return &coalescedPacket{buffer: getPacketBuffer()}, nil
}) })
mconn.EXPECT().Write(gomock.Any()) mconn.EXPECT().Write(gomock.Any())
sess.CloseWithError(0x1337, "test error") sess.CloseWithError(0x1337, "test error")
@ -452,12 +450,12 @@ var _ = Describe("Session", func() {
streamManager.EXPECT().CloseWithError(testErr) streamManager.EXPECT().CloseWithError(testErr)
expectReplaceWithClosed() expectReplaceWithClosed()
cryptoSetup.EXPECT().Close() cryptoSetup.EXPECT().Close()
packer.EXPECT().PackConnectionClose(gomock.Any()).DoAndReturn(func(f *wire.ConnectionCloseFrame) (*packedPacket, error) { packer.EXPECT().PackConnectionClose(gomock.Any()).DoAndReturn(func(quicErr *qerr.QuicError) (*coalescedPacket, error) {
Expect(f.IsApplicationError).To(BeFalse()) Expect(quicErr.IsApplicationError()).To(BeFalse())
Expect(f.FrameType).To(BeEquivalentTo(0x42)) Expect(quicErr.FrameType).To(BeEquivalentTo(0x42))
Expect(f.ErrorCode).To(BeEquivalentTo(0x1337)) Expect(quicErr.ErrorCode).To(BeEquivalentTo(0x1337))
Expect(f.ReasonPhrase).To(Equal("test error")) Expect(quicErr.ErrorMessage).To(Equal("test error"))
return &packedPacket{buffer: getPacketBuffer()}, nil return &coalescedPacket{buffer: getPacketBuffer()}, nil
}) })
mconn.EXPECT().Write(gomock.Any()) mconn.EXPECT().Write(gomock.Any())
sess.closeLocal(testErr) sess.closeLocal(testErr)
@ -465,23 +463,6 @@ var _ = Describe("Session", func() {
Expect(sess.Context().Done()).To(BeClosed()) 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() { It("closes the session in order to recreate it", func() {
streamManager.EXPECT().CloseWithError(gomock.Any()) streamManager.EXPECT().CloseWithError(gomock.Any())
sessionRunner.EXPECT().Remove(gomock.Any()).AnyTimes() sessionRunner.EXPECT().Remove(gomock.Any()).AnyTimes()
@ -507,7 +488,7 @@ var _ = Describe("Session", func() {
streamManager.EXPECT().CloseWithError(gomock.Any()) streamManager.EXPECT().CloseWithError(gomock.Any())
expectReplaceWithClosed() expectReplaceWithClosed()
cryptoSetup.EXPECT().Close() 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{}) returned := make(chan struct{})
go func() { go func() {
defer GinkgoRecover() defer GinkgoRecover()
@ -596,7 +577,7 @@ var _ = Describe("Session", func() {
unpacker.EXPECT().Unpack(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, handshake.ErrDecryptionFailed) unpacker.EXPECT().Unpack(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, handshake.ErrDecryptionFailed)
streamManager.EXPECT().CloseWithError(gomock.Any()) streamManager.EXPECT().CloseWithError(gomock.Any())
cryptoSetup.EXPECT().Close() 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() { go func() {
defer GinkgoRecover() defer GinkgoRecover()
cryptoSetup.EXPECT().RunHandshake().MaxTimes(1) 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) unpacker.EXPECT().Unpack(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, wire.ErrInvalidReservedBits)
streamManager.EXPECT().CloseWithError(gomock.Any()) streamManager.EXPECT().CloseWithError(gomock.Any())
cryptoSetup.EXPECT().Close() 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{}) done := make(chan struct{})
go func() { go func() {
defer GinkgoRecover() defer GinkgoRecover()
@ -642,7 +623,7 @@ var _ = Describe("Session", func() {
unpacker.EXPECT().Unpack(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, testErr) unpacker.EXPECT().Unpack(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, testErr)
streamManager.EXPECT().CloseWithError(gomock.Any()) streamManager.EXPECT().CloseWithError(gomock.Any())
cryptoSetup.EXPECT().Close() 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) runErr := make(chan error)
go func() { go func() {
defer GinkgoRecover() defer GinkgoRecover()
@ -668,7 +649,7 @@ var _ = Describe("Session", func() {
}, nil) }, nil)
streamManager.EXPECT().CloseWithError(gomock.Any()) streamManager.EXPECT().CloseWithError(gomock.Any())
cryptoSetup.EXPECT().Close() 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{}) done := make(chan struct{})
go func() { go func() {
defer GinkgoRecover() defer GinkgoRecover()
@ -864,7 +845,7 @@ var _ = Describe("Session", func() {
AfterEach(func() { AfterEach(func() {
streamManager.EXPECT().CloseWithError(gomock.Any()) 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() expectReplaceWithClosed()
cryptoSetup.EXPECT().Close() cryptoSetup.EXPECT().Close()
mconn.EXPECT().Write(gomock.Any()) mconn.EXPECT().Write(gomock.Any())
@ -995,7 +976,7 @@ var _ = Describe("Session", func() {
AfterEach(func() { AfterEach(func() {
// make the go routine return // 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() expectReplaceWithClosed()
cryptoSetup.EXPECT().Close() cryptoSetup.EXPECT().Close()
mconn.EXPECT().Write(gomock.Any()) mconn.EXPECT().Write(gomock.Any())
@ -1114,7 +1095,7 @@ var _ = Describe("Session", func() {
// make the go routine return // make the go routine return
expectReplaceWithClosed() expectReplaceWithClosed()
streamManager.EXPECT().CloseWithError(gomock.Any()) 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() cryptoSetup.EXPECT().Close()
mconn.EXPECT().Write(gomock.Any()) mconn.EXPECT().Write(gomock.Any())
sess.shutdown() sess.shutdown()
@ -1236,7 +1217,7 @@ var _ = Describe("Session", func() {
// make sure the go routine returns // make sure the go routine returns
streamManager.EXPECT().CloseWithError(gomock.Any()) streamManager.EXPECT().CloseWithError(gomock.Any())
expectReplaceWithClosed() expectReplaceWithClosed()
packer.EXPECT().PackConnectionClose(gomock.Any()).Return(&packedPacket{buffer: getPacketBuffer()}, nil) packer.EXPECT().PackConnectionClose(gomock.Any()).Return(&coalescedPacket{buffer: getPacketBuffer()}, nil)
cryptoSetup.EXPECT().Close() cryptoSetup.EXPECT().Close()
mconn.EXPECT().Write(gomock.Any()) mconn.EXPECT().Write(gomock.Any())
sess.shutdown() sess.shutdown()
@ -1272,7 +1253,7 @@ var _ = Describe("Session", func() {
// make sure the go routine returns // make sure the go routine returns
streamManager.EXPECT().CloseWithError(gomock.Any()) streamManager.EXPECT().CloseWithError(gomock.Any())
expectReplaceWithClosed() expectReplaceWithClosed()
packer.EXPECT().PackConnectionClose(gomock.Any()).Return(&packedPacket{buffer: getPacketBuffer()}, nil) packer.EXPECT().PackConnectionClose(gomock.Any()).Return(&coalescedPacket{buffer: getPacketBuffer()}, nil)
cryptoSetup.EXPECT().Close() cryptoSetup.EXPECT().Close()
mconn.EXPECT().Write(gomock.Any()) mconn.EXPECT().Write(gomock.Any())
sess.shutdown() sess.shutdown()
@ -1313,7 +1294,7 @@ var _ = Describe("Session", func() {
// make sure the go routine returns // make sure the go routine returns
streamManager.EXPECT().CloseWithError(gomock.Any()) streamManager.EXPECT().CloseWithError(gomock.Any())
expectReplaceWithClosed() expectReplaceWithClosed()
packer.EXPECT().PackConnectionClose(gomock.Any()).Return(&packedPacket{buffer: getPacketBuffer()}, nil) packer.EXPECT().PackConnectionClose(gomock.Any()).Return(&coalescedPacket{buffer: getPacketBuffer()}, nil)
cryptoSetup.EXPECT().Close() cryptoSetup.EXPECT().Close()
mconn.EXPECT().Write(gomock.Any()) mconn.EXPECT().Write(gomock.Any())
sess.shutdown() sess.shutdown()
@ -1324,7 +1305,7 @@ var _ = Describe("Session", func() {
packer.EXPECT().PackCoalescedPacket().AnyTimes() packer.EXPECT().PackCoalescedPacket().AnyTimes()
streamManager.EXPECT().CloseWithError(gomock.Any()) streamManager.EXPECT().CloseWithError(gomock.Any())
expectReplaceWithClosed() expectReplaceWithClosed()
packer.EXPECT().PackConnectionClose(gomock.Any()).Return(&packedPacket{buffer: getPacketBuffer()}, nil) packer.EXPECT().PackConnectionClose(gomock.Any()).Return(&coalescedPacket{buffer: getPacketBuffer()}, nil)
cryptoSetup.EXPECT().Close() cryptoSetup.EXPECT().Close()
go func() { go func() {
defer GinkgoRecover() defer GinkgoRecover()
@ -1369,7 +1350,7 @@ var _ = Describe("Session", func() {
// make sure the go routine returns // make sure the go routine returns
streamManager.EXPECT().CloseWithError(gomock.Any()) streamManager.EXPECT().CloseWithError(gomock.Any())
expectReplaceWithClosed() expectReplaceWithClosed()
packer.EXPECT().PackConnectionClose(gomock.Any()).Return(&packedPacket{buffer: getPacketBuffer()}, nil) packer.EXPECT().PackConnectionClose(gomock.Any()).Return(&coalescedPacket{buffer: getPacketBuffer()}, nil)
cryptoSetup.EXPECT().Close() cryptoSetup.EXPECT().Close()
sess.shutdown() sess.shutdown()
Eventually(sess.Context().Done()).Should(BeClosed()) Eventually(sess.Context().Done()).Should(BeClosed())
@ -1385,7 +1366,7 @@ var _ = Describe("Session", func() {
}() }()
streamManager.EXPECT().CloseWithError(gomock.Any()) streamManager.EXPECT().CloseWithError(gomock.Any())
expectReplaceWithClosed() expectReplaceWithClosed()
packer.EXPECT().PackConnectionClose(gomock.Any()).Return(&packedPacket{buffer: getPacketBuffer()}, nil) packer.EXPECT().PackConnectionClose(gomock.Any()).Return(&coalescedPacket{buffer: getPacketBuffer()}, nil)
cryptoSetup.EXPECT().Close() cryptoSetup.EXPECT().Close()
mconn.EXPECT().Write(gomock.Any()) mconn.EXPECT().Write(gomock.Any())
sess.shutdown() sess.shutdown()
@ -1404,7 +1385,7 @@ var _ = Describe("Session", func() {
}() }()
streamManager.EXPECT().CloseWithError(gomock.Any()) streamManager.EXPECT().CloseWithError(gomock.Any())
expectReplaceWithClosed() expectReplaceWithClosed()
packer.EXPECT().PackConnectionClose(gomock.Any()).Return(&packedPacket{buffer: getPacketBuffer()}, nil) packer.EXPECT().PackConnectionClose(gomock.Any()).Return(&coalescedPacket{buffer: getPacketBuffer()}, nil)
cryptoSetup.EXPECT().Close() cryptoSetup.EXPECT().Close()
mconn.EXPECT().Write(gomock.Any()) mconn.EXPECT().Write(gomock.Any())
Expect(sess.CloseWithError(0x1337, testErr.Error())).To(Succeed()) Expect(sess.CloseWithError(0x1337, testErr.Error())).To(Succeed())
@ -1441,7 +1422,7 @@ var _ = Describe("Session", func() {
Expect(s).To(BeAssignableToTypeOf(&closedLocalSession{})) Expect(s).To(BeAssignableToTypeOf(&closedLocalSession{}))
s.shutdown() s.shutdown()
}).Times(4) // initial connection ID + initial client dest conn ID + 2 newly issued conn IDs }).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() cryptoSetup.EXPECT().Close()
mconn.EXPECT().Write(gomock.Any()) mconn.EXPECT().Write(gomock.Any())
sess.shutdown() sess.shutdown()
@ -1473,7 +1454,7 @@ var _ = Describe("Session", func() {
// make the go routine return // make the go routine return
expectReplaceWithClosed() expectReplaceWithClosed()
streamManager.EXPECT().CloseWithError(gomock.Any()) 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() cryptoSetup.EXPECT().Close()
mconn.EXPECT().Write(gomock.Any()) mconn.EXPECT().Write(gomock.Any())
sess.shutdown() sess.shutdown()
@ -1571,9 +1552,9 @@ var _ = Describe("Session", func() {
sess.handshakeComplete = false sess.handshakeComplete = false
sess.config.MaxIdleTimeout = 9999 * time.Second sess.config.MaxIdleTimeout = 9999 * time.Second
sess.lastPacketReceivedTime = time.Now().Add(-time.Minute) sess.lastPacketReceivedTime = time.Now().Add(-time.Minute)
packer.EXPECT().PackConnectionClose(gomock.Any()).DoAndReturn(func(f *wire.ConnectionCloseFrame) (*packedPacket, error) { packer.EXPECT().PackConnectionClose(gomock.Any()).DoAndReturn(func(quicErr *qerr.QuicError) (*coalescedPacket, error) {
Expect(f.ErrorCode).To(Equal(qerr.NoError)) Expect(quicErr.ErrorCode).To(Equal(qerr.NoError))
return &packedPacket{buffer: getPacketBuffer()}, nil return &coalescedPacket{buffer: getPacketBuffer()}, nil
}) })
// the handshake timeout is irrelevant here, since it depends on the time the session was created, // the handshake timeout is irrelevant here, since it depends on the time the session was created,
// and not on the last network activity // and not on the last network activity
@ -1629,7 +1610,7 @@ var _ = Describe("Session", func() {
}() }()
Consistently(sess.Context().Done()).ShouldNot(BeClosed()) Consistently(sess.Context().Done()).ShouldNot(BeClosed())
// make the go routine return // 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() expectReplaceWithClosed()
cryptoSetup.EXPECT().Close() cryptoSetup.EXPECT().Close()
mconn.EXPECT().Write(gomock.Any()) mconn.EXPECT().Write(gomock.Any())
@ -1805,7 +1786,7 @@ var _ = Describe("Client Session", func() {
PacketNumberLen: protocol.PacketNumberLen2, PacketNumberLen: protocol.PacketNumberLen2,
}, []byte{0}))).To(BeTrue()) }, []byte{0}))).To(BeTrue())
// make sure the go routine returns // 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() expectReplaceWithClosed()
cryptoSetup.EXPECT().Close() cryptoSetup.EXPECT().Close()
mconn.EXPECT().Write(gomock.Any()) mconn.EXPECT().Write(gomock.Any())
@ -1930,7 +1911,7 @@ var _ = Describe("Client Session", func() {
Expect(s).To(BeAssignableToTypeOf(&closedLocalSession{})) Expect(s).To(BeAssignableToTypeOf(&closedLocalSession{}))
s.shutdown() 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() cryptoSetup.EXPECT().Close()
mconn.EXPECT().Write(gomock.Any()) mconn.EXPECT().Write(gomock.Any())
} }