diff --git a/qlog/frame_test.go b/qlog/frame_test.go index bae6ea11..b1935485 100644 --- a/qlog/frame_test.go +++ b/qlog/frame_test.go @@ -12,34 +12,11 @@ import ( ) var _ = Describe("Frames", func() { - // marshal the frame check := func(f wire.Frame, expected map[string]interface{}) { data, err := transformFrame(f).MarshalJSON() ExpectWithOffset(1, err).ToNot(HaveOccurred()) ExpectWithOffset(1, json.Valid(data)).To(BeTrue()) - // unmarshal the frame - m := make(map[string](interface{})) - ExpectWithOffset(1, json.Unmarshal(data, &m)).To(Succeed()) - ExpectWithOffset(1, m).To(HaveLen(len(expected))) - for key, value := range expected { - switch value.(type) { - case string: - ExpectWithOffset(1, m).To(HaveKeyWithValue(key, value)) - case int: - ExpectWithOffset(1, m).To(HaveKeyWithValue(key, float64(value.(int)))) - case bool: - ExpectWithOffset(1, m).To(HaveKeyWithValue(key, value.(bool))) - case [][]string: // used in the ACK frame - ExpectWithOffset(1, m).To(HaveKey(key)) - for i, l := range value.([][]string) { - for j, s := range l { - ExpectWithOffset(1, m[key].([]interface{})[i].([]interface{})[j].(string)).To(Equal(s)) - } - } - default: - Fail("unexpected type") - } - } + checkEncoding(data, expected) } It("marshals PING frames", func() { diff --git a/qlog/packet_header.go b/qlog/packet_header.go new file mode 100644 index 00000000..00e005c7 --- /dev/null +++ b/qlog/packet_header.go @@ -0,0 +1,54 @@ +package qlog + +import ( + "encoding/json" + "fmt" + + "github.com/lucas-clemente/quic-go/internal/protocol" + "github.com/lucas-clemente/quic-go/internal/wire" +) + +type versionNumber protocol.VersionNumber + +func (v versionNumber) MarshalJSON() ([]byte, error) { + return escapeStr(fmt.Sprintf("%x", v)), nil +} + +func transformHeader(hdr *wire.ExtendedHeader) *packetHeader { + return &packetHeader{ + PacketNumber: hdr.PacketNumber, + PayloadLength: hdr.Length, + SrcConnectionID: hdr.SrcConnectionID, + DestConnectionID: hdr.DestConnectionID, + Version: hdr.Version, + } +} + +type packetHeader struct { + PacketNumber protocol.PacketNumber `json:"packet_number,string"` + PacketSize protocol.ByteCount `json:"packet_size,omitempty"` + PayloadLength protocol.ByteCount `json:"payload_length,omitempty"` + + Version protocol.VersionNumber `json:"version,omitempty"` + SrcConnectionID protocol.ConnectionID `json:"scid,string,omitempty"` + DestConnectionID protocol.ConnectionID `json:"dcid,string,omitempty"` +} + +func (h packetHeader) MarshalJSON() ([]byte, error) { + type Alias packetHeader + return json.Marshal(&struct { + SrcConnectionIDLen int `json:"scil,string,omitempty"` + SrcConnectionID connectionID `json:"scid,string,omitempty"` + DestConnectionIDLen int `json:"dcil,string,omitempty"` + DestConnectionID connectionID `json:"dcid,string,omitempty"` + Version versionNumber `json:"version,omitempty"` + Alias + }{ + Alias: (Alias)(h), + SrcConnectionIDLen: h.SrcConnectionID.Len(), + SrcConnectionID: connectionID(h.SrcConnectionID), + DestConnectionIDLen: h.DestConnectionID.Len(), + DestConnectionID: connectionID(h.DestConnectionID), + Version: versionNumber(h.Version), + }) +} diff --git a/qlog/packet_header_test.go b/qlog/packet_header_test.go new file mode 100644 index 00000000..75b788c3 --- /dev/null +++ b/qlog/packet_header_test.go @@ -0,0 +1,85 @@ +package qlog + +import ( + "encoding/json" + + "github.com/lucas-clemente/quic-go/internal/protocol" + "github.com/lucas-clemente/quic-go/internal/wire" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" +) + +var _ = Describe("Packet Header", func() { + check := func(hdr *wire.ExtendedHeader, expected map[string]interface{}) { + data, err := json.Marshal(transformHeader(hdr)) + ExpectWithOffset(1, err).ToNot(HaveOccurred()) + ExpectWithOffset(1, json.Valid(data)).To(BeTrue()) + checkEncoding(data, expected) + } + + It("marshals a header", func() { + check( + &wire.ExtendedHeader{PacketNumber: 42}, + map[string]interface{}{ + "packet_number": "42", + }, + ) + }) + + It("marshals a header with a payload length", func() { + check( + &wire.ExtendedHeader{ + PacketNumber: 42, + Header: wire.Header{Length: 123}, + }, + map[string]interface{}{ + "packet_number": "42", + "payload_length": 123, + }, + ) + }) + + It("marshals a header with a source connection ID", func() { + check( + &wire.ExtendedHeader{ + PacketNumber: 42, + Header: wire.Header{ + SrcConnectionID: protocol.ConnectionID{0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff}, + }, + }, + map[string]interface{}{ + "packet_number": "42", + "scil": "16", + "scid": "00112233445566778899aabbccddeeff", + }, + ) + }) + + It("marshals a header with a destination connection ID", func() { + check( + &wire.ExtendedHeader{ + PacketNumber: 42, + Header: wire.Header{DestConnectionID: protocol.ConnectionID{0xde, 0xad, 0xbe, 0xef}}, + }, + map[string]interface{}{ + "packet_number": "42", + "dcil": "4", + "dcid": "deadbeef", + }, + ) + }) + + It("marshals a header with a version number", func() { + check( + &wire.ExtendedHeader{ + PacketNumber: 42, + Header: wire.Header{Version: protocol.VersionNumber(0xdecafbad)}, + }, + map[string]interface{}{ + "packet_number": "42", + "version": "decafbad", + }, + ) + }) +}) diff --git a/qlog/qlog_suite_test.go b/qlog/qlog_suite_test.go index 935475dd..f47c5e60 100644 --- a/qlog/qlog_suite_test.go +++ b/qlog/qlog_suite_test.go @@ -1,6 +1,7 @@ package qlog import ( + "encoding/json" "testing" . "github.com/onsi/ginkgo" @@ -11,3 +12,29 @@ func TestQlog(t *testing.T) { RegisterFailHandler(Fail) RunSpecs(t, "qlog Suite") } + +func checkEncoding(data []byte, expected map[string](interface{})) { + // unmarshal the data + m := make(map[string](interface{})) + ExpectWithOffset(1, json.Unmarshal(data, &m)).To(Succeed()) + ExpectWithOffset(1, m).To(HaveLen(len(expected))) + for key, value := range expected { + switch value.(type) { + case string: + ExpectWithOffset(1, m).To(HaveKeyWithValue(key, value)) + case int: + ExpectWithOffset(1, m).To(HaveKeyWithValue(key, float64(value.(int)))) + case bool: + ExpectWithOffset(1, m).To(HaveKeyWithValue(key, value.(bool))) + case [][]string: // used in the ACK frame + ExpectWithOffset(1, m).To(HaveKey(key)) + for i, l := range value.([][]string) { + for j, s := range l { + ExpectWithOffset(1, m[key].([]interface{})[i].([]interface{})[j].(string)).To(Equal(s)) + } + } + default: + Fail("unexpected type") + } + } +}