diff --git a/qlog/frame.go b/qlog/frame.go index 9926cf2e3..b820997bb 100644 --- a/qlog/frame.go +++ b/qlog/frame.go @@ -236,8 +236,12 @@ func marshalConnectionCloseFrame(enc *gojay.Encoder, f *wire.ConnectionCloseFram } enc.StringKey("frame_type", "connection_close") enc.StringKey("error_space", errorSpace) - enc.Int64Key("error_code", int64(f.ErrorCode)) - enc.Int64Key("raw_error_code", int64(f.ErrorCode)) + if errName := transportError(f.ErrorCode).String(); len(errName) > 0 { + enc.StringKey("error_code", errName) + } else { + enc.Uint64Key("error_code", uint64(f.ErrorCode)) + } + enc.Uint64Key("raw_error_code", uint64(f.ErrorCode)) enc.StringKey("reason", f.ReasonPhrase) } diff --git a/qlog/frame_test.go b/qlog/frame_test.go index 79d9a8543..a3bfd93ba 100644 --- a/qlog/frame_test.go +++ b/qlog/frame_test.go @@ -5,6 +5,8 @@ import ( "encoding/json" "time" + "github.com/lucas-clemente/quic-go/internal/qerr" + "github.com/francoispqt/gojay" "github.com/lucas-clemente/quic-go/internal/protocol" @@ -325,14 +327,14 @@ var _ = Describe("Frames", func() { It("marshals CONNECTION_CLOSE frames, for transport error codes", func() { check( &wire.ConnectionCloseFrame{ - ErrorCode: 1337, + ErrorCode: qerr.FlowControlError, ReasonPhrase: "lorem ipsum", }, map[string]interface{}{ "frame_type": "connection_close", "error_space": "transport", - "error_code": 1337, - "raw_error_code": 1337, + "error_code": "flow_control_error", + "raw_error_code": int(qerr.FlowControlError), "reason": "lorem ipsum", }, ) diff --git a/qlog/types.go b/qlog/types.go index baffaedd0..a8dd74045 100644 --- a/qlog/types.go +++ b/qlog/types.go @@ -4,6 +4,7 @@ import ( "fmt" "github.com/lucas-clemente/quic-go/internal/protocol" + "github.com/lucas-clemente/quic-go/internal/qerr" ) type owner uint8 @@ -264,3 +265,40 @@ func (r PacketDropReason) String() string { panic("unknown packet drop reason") } } + +type transportError uint64 + +func (e transportError) String() string { + switch qerr.ErrorCode(e) { + case qerr.NoError: + return "no_error" + case qerr.InternalError: + return "internal_error" + case qerr.ServerBusy: + return "server_busy" + case qerr.FlowControlError: + return "flow_control_error" + case qerr.StreamLimitError: + return "stream_limit_error" + case qerr.StreamStateError: + return "stream_state_error" + case qerr.FinalSizeError: + return "final_size_error" + case qerr.FrameEncodingError: + return "frame_encoding_error" + case qerr.TransportParameterError: + return "transport_parameter_error" + case qerr.ConnectionIDLimitError: + return "connection_id_limit_error" + case qerr.ProtocolViolation: + return "protocol_violation" + case qerr.InvalidToken: + return "invalid_token" + case qerr.ApplicationError: + return "application_error" + case qerr.CryptoBufferExceeded: + return "crypto_buffer_exceeded" + default: + return "" + } +} diff --git a/qlog/types_test.go b/qlog/types_test.go index 9d0b92609..df2ea0939 100644 --- a/qlog/types_test.go +++ b/qlog/types_test.go @@ -1,7 +1,16 @@ package qlog import ( + "go/ast" + "go/parser" + "go/token" + "path" + "runtime" + "strconv" + "github.com/lucas-clemente/quic-go/internal/protocol" + "github.com/lucas-clemente/quic-go/internal/qerr" + . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) @@ -56,4 +65,42 @@ var _ = Describe("Types", func() { Expect(PacketDropDOSPrevention.String()).To(Equal("dos_prevention")) Expect(PacketDropUnsupportedVersion.String()).To(Equal("unsupported_version")) }) + + Context("transport errors", func() { + It("has a string representation for every error code", func() { + // We parse the error code file, extract all constants, and verify that + // each of them has a string version. Go FTW! + _, thisfile, _, ok := runtime.Caller(0) + if !ok { + panic("Failed to get current frame") + } + filename := path.Join(path.Dir(thisfile), "../internal/qerr/error_codes.go") + fileAst, err := parser.ParseFile(token.NewFileSet(), filename, nil, 0) + Expect(err).NotTo(HaveOccurred()) + constSpecs := fileAst.Decls[2].(*ast.GenDecl).Specs + Expect(len(constSpecs)).To(BeNumerically(">", 4)) // at time of writing + for _, c := range constSpecs { + valString := c.(*ast.ValueSpec).Values[0].(*ast.BasicLit).Value + val, err := strconv.ParseInt(valString, 0, 64) + Expect(err).NotTo(HaveOccurred()) + Expect(transportError(val).String()).ToNot(BeEmpty()) + } + }) + + It("has a string representation for transport errors", func() { + Expect(transportError(qerr.NoError).String()).To(Equal("no_error")) + Expect(transportError(qerr.InternalError).String()).To(Equal("internal_error")) + Expect(transportError(qerr.ServerBusy).String()).To(Equal("server_busy")) + Expect(transportError(qerr.FlowControlError).String()).To(Equal("flow_control_error")) + Expect(transportError(qerr.StreamLimitError).String()).To(Equal("stream_limit_error")) + Expect(transportError(qerr.StreamStateError).String()).To(Equal("stream_state_error")) + Expect(transportError(qerr.FrameEncodingError).String()).To(Equal("frame_encoding_error")) + Expect(transportError(qerr.ConnectionIDLimitError).String()).To(Equal("connection_id_limit_error")) + Expect(transportError(qerr.ProtocolViolation).String()).To(Equal("protocol_violation")) + Expect(transportError(qerr.InvalidToken).String()).To(Equal("invalid_token")) + Expect(transportError(qerr.ApplicationError).String()).To(Equal("application_error")) + Expect(transportError(qerr.CryptoBufferExceeded).String()).To(Equal("crypto_buffer_exceeded")) + Expect(transportError(1337).String()).To(BeEmpty()) + }) + }) })