1package qlog
2
3import (
4	"fmt"
5
6	"github.com/lucas-clemente/quic-go/internal/wire"
7	"github.com/lucas-clemente/quic-go/logging"
8
9	"github.com/francoispqt/gojay"
10)
11
12type frame struct {
13	Frame logging.Frame
14}
15
16var _ gojay.MarshalerJSONObject = frame{}
17
18var _ gojay.MarshalerJSONArray = frames{}
19
20func (f frame) MarshalJSONObject(enc *gojay.Encoder) {
21	switch frame := f.Frame.(type) {
22	case *logging.PingFrame:
23		marshalPingFrame(enc, frame)
24	case *logging.AckFrame:
25		marshalAckFrame(enc, frame)
26	case *logging.ResetStreamFrame:
27		marshalResetStreamFrame(enc, frame)
28	case *logging.StopSendingFrame:
29		marshalStopSendingFrame(enc, frame)
30	case *logging.CryptoFrame:
31		marshalCryptoFrame(enc, frame)
32	case *logging.NewTokenFrame:
33		marshalNewTokenFrame(enc, frame)
34	case *logging.StreamFrame:
35		marshalStreamFrame(enc, frame)
36	case *logging.MaxDataFrame:
37		marshalMaxDataFrame(enc, frame)
38	case *logging.MaxStreamDataFrame:
39		marshalMaxStreamDataFrame(enc, frame)
40	case *logging.MaxStreamsFrame:
41		marshalMaxStreamsFrame(enc, frame)
42	case *logging.DataBlockedFrame:
43		marshalDataBlockedFrame(enc, frame)
44	case *logging.StreamDataBlockedFrame:
45		marshalStreamDataBlockedFrame(enc, frame)
46	case *logging.StreamsBlockedFrame:
47		marshalStreamsBlockedFrame(enc, frame)
48	case *logging.NewConnectionIDFrame:
49		marshalNewConnectionIDFrame(enc, frame)
50	case *logging.RetireConnectionIDFrame:
51		marshalRetireConnectionIDFrame(enc, frame)
52	case *logging.PathChallengeFrame:
53		marshalPathChallengeFrame(enc, frame)
54	case *logging.PathResponseFrame:
55		marshalPathResponseFrame(enc, frame)
56	case *logging.ConnectionCloseFrame:
57		marshalConnectionCloseFrame(enc, frame)
58	case *logging.HandshakeDoneFrame:
59		marshalHandshakeDoneFrame(enc, frame)
60	default:
61		panic("unknown frame type")
62	}
63}
64
65func (f frame) IsNil() bool { return false }
66
67type frames []frame
68
69func (fs frames) IsNil() bool { return fs == nil }
70func (fs frames) MarshalJSONArray(enc *gojay.Encoder) {
71	for _, f := range fs {
72		enc.Object(f)
73	}
74}
75
76func marshalPingFrame(enc *gojay.Encoder, _ *wire.PingFrame) {
77	enc.StringKey("frame_type", "ping")
78}
79
80type ackRanges []wire.AckRange
81
82func (ars ackRanges) MarshalJSONArray(enc *gojay.Encoder) {
83	for _, r := range ars {
84		enc.Array(ackRange(r))
85	}
86}
87
88func (ars ackRanges) IsNil() bool { return false }
89
90type ackRange wire.AckRange
91
92func (ar ackRange) MarshalJSONArray(enc *gojay.Encoder) {
93	enc.AddInt64(int64(ar.Smallest))
94	if ar.Smallest != ar.Largest {
95		enc.AddInt64(int64(ar.Largest))
96	}
97}
98
99func (ar ackRange) IsNil() bool { return false }
100
101func marshalAckFrame(enc *gojay.Encoder, f *logging.AckFrame) {
102	enc.StringKey("frame_type", "ack")
103	enc.FloatKeyOmitEmpty("ack_delay", milliseconds(f.DelayTime))
104	enc.ArrayKey("acked_ranges", ackRanges(f.AckRanges))
105	if hasECN := f.ECT0 > 0 || f.ECT1 > 0 || f.ECNCE > 0; hasECN {
106		enc.Uint64Key("ect0", f.ECT0)
107		enc.Uint64Key("ect1", f.ECT1)
108		enc.Uint64Key("ce", f.ECNCE)
109	}
110}
111
112func marshalResetStreamFrame(enc *gojay.Encoder, f *logging.ResetStreamFrame) {
113	enc.StringKey("frame_type", "reset_stream")
114	enc.Int64Key("stream_id", int64(f.StreamID))
115	enc.Int64Key("error_code", int64(f.ErrorCode))
116	enc.Int64Key("final_size", int64(f.FinalSize))
117}
118
119func marshalStopSendingFrame(enc *gojay.Encoder, f *logging.StopSendingFrame) {
120	enc.StringKey("frame_type", "stop_sending")
121	enc.Int64Key("stream_id", int64(f.StreamID))
122	enc.Int64Key("error_code", int64(f.ErrorCode))
123}
124
125func marshalCryptoFrame(enc *gojay.Encoder, f *logging.CryptoFrame) {
126	enc.StringKey("frame_type", "crypto")
127	enc.Int64Key("offset", int64(f.Offset))
128	enc.Int64Key("length", int64(f.Length))
129}
130
131func marshalNewTokenFrame(enc *gojay.Encoder, f *logging.NewTokenFrame) {
132	enc.StringKey("frame_type", "new_token")
133	enc.IntKey("length", len(f.Token))
134	enc.StringKey("token", fmt.Sprintf("%x", f.Token))
135}
136
137func marshalStreamFrame(enc *gojay.Encoder, f *logging.StreamFrame) {
138	enc.StringKey("frame_type", "stream")
139	enc.Int64Key("stream_id", int64(f.StreamID))
140	enc.Int64Key("offset", int64(f.Offset))
141	enc.IntKey("length", int(f.Length))
142	enc.BoolKeyOmitEmpty("fin", f.Fin)
143}
144
145func marshalMaxDataFrame(enc *gojay.Encoder, f *logging.MaxDataFrame) {
146	enc.StringKey("frame_type", "max_data")
147	enc.Int64Key("maximum", int64(f.MaximumData))
148}
149
150func marshalMaxStreamDataFrame(enc *gojay.Encoder, f *logging.MaxStreamDataFrame) {
151	enc.StringKey("frame_type", "max_stream_data")
152	enc.Int64Key("stream_id", int64(f.StreamID))
153	enc.Int64Key("maximum", int64(f.MaximumStreamData))
154}
155
156func marshalMaxStreamsFrame(enc *gojay.Encoder, f *logging.MaxStreamsFrame) {
157	enc.StringKey("frame_type", "max_streams")
158	enc.StringKey("stream_type", streamType(f.Type).String())
159	enc.Int64Key("maximum", int64(f.MaxStreamNum))
160}
161
162func marshalDataBlockedFrame(enc *gojay.Encoder, f *logging.DataBlockedFrame) {
163	enc.StringKey("frame_type", "data_blocked")
164	enc.Int64Key("limit", int64(f.MaximumData))
165}
166
167func marshalStreamDataBlockedFrame(enc *gojay.Encoder, f *logging.StreamDataBlockedFrame) {
168	enc.StringKey("frame_type", "stream_data_blocked")
169	enc.Int64Key("stream_id", int64(f.StreamID))
170	enc.Int64Key("limit", int64(f.MaximumStreamData))
171}
172
173func marshalStreamsBlockedFrame(enc *gojay.Encoder, f *logging.StreamsBlockedFrame) {
174	enc.StringKey("frame_type", "streams_blocked")
175	enc.StringKey("stream_type", streamType(f.Type).String())
176	enc.Int64Key("limit", int64(f.StreamLimit))
177}
178
179func marshalNewConnectionIDFrame(enc *gojay.Encoder, f *logging.NewConnectionIDFrame) {
180	enc.StringKey("frame_type", "new_connection_id")
181	enc.Int64Key("sequence_number", int64(f.SequenceNumber))
182	enc.Int64Key("retire_prior_to", int64(f.RetirePriorTo))
183	enc.IntKey("length", f.ConnectionID.Len())
184	enc.StringKey("connection_id", connectionID(f.ConnectionID).String())
185	enc.StringKey("stateless_reset_token", fmt.Sprintf("%x", f.StatelessResetToken))
186}
187
188func marshalRetireConnectionIDFrame(enc *gojay.Encoder, f *logging.RetireConnectionIDFrame) {
189	enc.StringKey("frame_type", "retire_connection_id")
190	enc.Int64Key("sequence_number", int64(f.SequenceNumber))
191}
192
193func marshalPathChallengeFrame(enc *gojay.Encoder, f *logging.PathChallengeFrame) {
194	enc.StringKey("frame_type", "path_challenge")
195	enc.StringKey("data", fmt.Sprintf("%x", f.Data[:]))
196}
197
198func marshalPathResponseFrame(enc *gojay.Encoder, f *logging.PathResponseFrame) {
199	enc.StringKey("frame_type", "path_response")
200	enc.StringKey("data", fmt.Sprintf("%x", f.Data[:]))
201}
202
203func marshalConnectionCloseFrame(enc *gojay.Encoder, f *logging.ConnectionCloseFrame) {
204	errorSpace := "transport"
205	if f.IsApplicationError {
206		errorSpace = "application"
207	}
208	enc.StringKey("frame_type", "connection_close")
209	enc.StringKey("error_space", errorSpace)
210	if errName := transportError(f.ErrorCode).String(); len(errName) > 0 {
211		enc.StringKey("error_code", errName)
212	} else {
213		enc.Uint64Key("error_code", uint64(f.ErrorCode))
214	}
215	enc.Uint64Key("raw_error_code", uint64(f.ErrorCode))
216	enc.StringKey("reason", f.ReasonPhrase)
217}
218
219func marshalHandshakeDoneFrame(enc *gojay.Encoder, _ *logging.HandshakeDoneFrame) {
220	enc.StringKey("frame_type", "handshake_done")
221}
222