use application-specific CONNECTION_CLOSE for application errors

This commit is contained in:
Marten Seemann
2019-11-08 13:46:20 +07:00
parent 914193cc9d
commit a472ac4731
4 changed files with 25 additions and 8 deletions

View File

@@ -39,6 +39,7 @@ func CryptoError(tlsAlert uint8, errorMessage string) *QuicError {
} }
} }
// ApplicationError creates a new QuicError instance for an application error
func ApplicationError(errorCode ErrorCode, errorMessage string) *QuicError { func ApplicationError(errorCode ErrorCode, errorMessage string) *QuicError {
return &QuicError{ return &QuicError{
ErrorCode: errorCode, ErrorCode: errorCode,
@@ -65,6 +66,11 @@ func (e *QuicError) IsCryptoError() bool {
return e.ErrorCode.isCryptoError() return e.ErrorCode.isCryptoError()
} }
// IsApplicationError says if this error is an application error
func (e *QuicError) IsApplicationError() bool {
return e.isApplicationError
}
// Temporary says if the error is temporary. // Temporary says if the error is temporary.
func (e *QuicError) Temporary() bool { func (e *QuicError) Temporary() bool {
return false return false

View File

@@ -11,6 +11,7 @@ var _ = Describe("QUIC Transport Errors", func() {
It("has a string representation", func() { It("has a string representation", func() {
err := Error(FlowControlError, "foobar") err := Error(FlowControlError, "foobar")
Expect(err.Timeout()).To(BeFalse()) Expect(err.Timeout()).To(BeFalse())
Expect(err.IsApplicationError()).To(BeFalse())
Expect(err.Error()).To(Equal("FLOW_CONTROL_ERROR: foobar")) Expect(err.Error()).To(Equal("FLOW_CONTROL_ERROR: foobar"))
}) })
@@ -38,13 +39,17 @@ var _ = Describe("QUIC Transport Errors", func() {
It("says if an error is a crypto error", func() { It("says if an error is a crypto error", func() {
Expect(Error(FlowControlError, "").IsCryptoError()).To(BeFalse()) Expect(Error(FlowControlError, "").IsCryptoError()).To(BeFalse())
Expect(CryptoError(42, "").IsCryptoError()).To(BeTrue()) err := CryptoError(42, "")
Expect(err.IsCryptoError()).To(BeTrue())
Expect(err.IsApplicationError()).To(BeFalse())
}) })
}) })
Context("application errors", func() { Context("application errors", func() {
It("has a string representation for errors with a message", func() { It("has a string representation for errors with a message", func() {
err := ApplicationError(0x42, "foobar") err := ApplicationError(0x42, "foobar")
Expect(err.IsApplicationError()).To(BeTrue())
Expect(err.Error()).To(Equal("Application error 0x42: foobar")) Expect(err.Error()).To(Equal("Application error 0x42: foobar"))
}) })

View File

@@ -1023,7 +1023,7 @@ func (s *session) Close() error {
} }
func (s *session) CloseWithError(code protocol.ApplicationErrorCode, desc string) error { func (s *session) CloseWithError(code protocol.ApplicationErrorCode, desc string) error {
s.closeLocal(qerr.Error(qerr.ErrorCode(code), desc)) s.closeLocal(qerr.ApplicationError(qerr.ErrorCode(code), desc))
<-s.ctx.Done() <-s.ctx.Done()
return nil return nil
} }
@@ -1250,8 +1250,9 @@ func (s *session) sendConnectionClose(quicErr *qerr.QuicError) ([]byte, error) {
reason = quicErr.ErrorMessage reason = quicErr.ErrorMessage
} }
packet, err := s.packer.PackConnectionClose(&wire.ConnectionCloseFrame{ packet, err := s.packer.PackConnectionClose(&wire.ConnectionCloseFrame{
ErrorCode: quicErr.ErrorCode, IsApplicationError: quicErr.IsApplicationError(),
ReasonPhrase: reason, ErrorCode: quicErr.ErrorCode,
ReasonPhrase: reason,
}) })
if err != nil { if err != nil {
return nil, err return nil, err

View File

@@ -449,12 +449,17 @@ var _ = Describe("Session", func() {
Expect(sess.Context().Done()).To(BeClosed()) Expect(sess.Context().Done()).To(BeClosed())
}) })
It("closes streams with proper error", func() { It("closes with an error", func() {
testErr := errors.New("test error") testErr := errors.New("test error")
streamManager.EXPECT().CloseWithError(qerr.Error(0x1337, testErr.Error())) streamManager.EXPECT().CloseWithError(qerr.ApplicationError(0x1337, testErr.Error()))
expectReplaceWithClosed() expectReplaceWithClosed()
cryptoSetup.EXPECT().Close() cryptoSetup.EXPECT().Close()
packer.EXPECT().PackConnectionClose(gomock.Any()).Return(&packedPacket{}, nil) 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{}, nil
})
sess.CloseWithError(0x1337, testErr.Error()) sess.CloseWithError(0x1337, testErr.Error())
Eventually(areSessionsRunning).Should(BeFalse()) Eventually(areSessionsRunning).Should(BeFalse())
Expect(sess.Context().Done()).To(BeClosed()) Expect(sess.Context().Done()).To(BeClosed())
@@ -1203,7 +1208,7 @@ var _ = Describe("Session", func() {
defer GinkgoRecover() defer GinkgoRecover()
cryptoSetup.EXPECT().RunHandshake().MaxTimes(1) cryptoSetup.EXPECT().RunHandshake().MaxTimes(1)
err := sess.run() err := sess.run()
Expect(err).To(MatchError(qerr.Error(0x1337, testErr.Error()))) Expect(err).To(MatchError(qerr.ApplicationError(0x1337, testErr.Error())))
close(done) close(done)
}() }()
streamManager.EXPECT().CloseWithError(gomock.Any()) streamManager.EXPECT().CloseWithError(gomock.Any())