golang.org/x/net@v0.25.1-0.20240516223405-c87a5b62e243/quic/frame_debug.go (about)

     1  // Copyright 2023 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  //go:build go1.21
     6  
     7  package quic
     8  
     9  import (
    10  	"fmt"
    11  	"log/slog"
    12  	"strconv"
    13  	"time"
    14  )
    15  
    16  // A debugFrame is a representation of the contents of a QUIC frame,
    17  // used for debug logs and testing but not the primary serving path.
    18  type debugFrame interface {
    19  	String() string
    20  	write(w *packetWriter) bool
    21  	LogValue() slog.Value
    22  }
    23  
    24  func parseDebugFrame(b []byte) (f debugFrame, n int) {
    25  	if len(b) == 0 {
    26  		return nil, -1
    27  	}
    28  	switch b[0] {
    29  	case frameTypePadding:
    30  		f, n = parseDebugFramePadding(b)
    31  	case frameTypePing:
    32  		f, n = parseDebugFramePing(b)
    33  	case frameTypeAck, frameTypeAckECN:
    34  		f, n = parseDebugFrameAck(b)
    35  	case frameTypeResetStream:
    36  		f, n = parseDebugFrameResetStream(b)
    37  	case frameTypeStopSending:
    38  		f, n = parseDebugFrameStopSending(b)
    39  	case frameTypeCrypto:
    40  		f, n = parseDebugFrameCrypto(b)
    41  	case frameTypeNewToken:
    42  		f, n = parseDebugFrameNewToken(b)
    43  	case frameTypeStreamBase, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f:
    44  		f, n = parseDebugFrameStream(b)
    45  	case frameTypeMaxData:
    46  		f, n = parseDebugFrameMaxData(b)
    47  	case frameTypeMaxStreamData:
    48  		f, n = parseDebugFrameMaxStreamData(b)
    49  	case frameTypeMaxStreamsBidi, frameTypeMaxStreamsUni:
    50  		f, n = parseDebugFrameMaxStreams(b)
    51  	case frameTypeDataBlocked:
    52  		f, n = parseDebugFrameDataBlocked(b)
    53  	case frameTypeStreamDataBlocked:
    54  		f, n = parseDebugFrameStreamDataBlocked(b)
    55  	case frameTypeStreamsBlockedBidi, frameTypeStreamsBlockedUni:
    56  		f, n = parseDebugFrameStreamsBlocked(b)
    57  	case frameTypeNewConnectionID:
    58  		f, n = parseDebugFrameNewConnectionID(b)
    59  	case frameTypeRetireConnectionID:
    60  		f, n = parseDebugFrameRetireConnectionID(b)
    61  	case frameTypePathChallenge:
    62  		f, n = parseDebugFramePathChallenge(b)
    63  	case frameTypePathResponse:
    64  		f, n = parseDebugFramePathResponse(b)
    65  	case frameTypeConnectionCloseTransport:
    66  		f, n = parseDebugFrameConnectionCloseTransport(b)
    67  	case frameTypeConnectionCloseApplication:
    68  		f, n = parseDebugFrameConnectionCloseApplication(b)
    69  	case frameTypeHandshakeDone:
    70  		f, n = parseDebugFrameHandshakeDone(b)
    71  	default:
    72  		return nil, -1
    73  	}
    74  	return f, n
    75  }
    76  
    77  // debugFramePadding is a sequence of PADDING frames.
    78  type debugFramePadding struct {
    79  	size int
    80  	to   int // alternate for writing packets: pad to
    81  }
    82  
    83  func parseDebugFramePadding(b []byte) (f debugFramePadding, n int) {
    84  	for n < len(b) && b[n] == frameTypePadding {
    85  		n++
    86  	}
    87  	f.size = n
    88  	return f, n
    89  }
    90  
    91  func (f debugFramePadding) String() string {
    92  	return fmt.Sprintf("PADDING*%v", f.size)
    93  }
    94  
    95  func (f debugFramePadding) write(w *packetWriter) bool {
    96  	if w.avail() == 0 {
    97  		return false
    98  	}
    99  	if f.to > 0 {
   100  		w.appendPaddingTo(f.to)
   101  		return true
   102  	}
   103  	for i := 0; i < f.size && w.avail() > 0; i++ {
   104  		w.b = append(w.b, frameTypePadding)
   105  	}
   106  	return true
   107  }
   108  
   109  func (f debugFramePadding) LogValue() slog.Value {
   110  	return slog.GroupValue(
   111  		slog.String("frame_type", "padding"),
   112  		slog.Int("length", f.size),
   113  	)
   114  }
   115  
   116  // debugFramePing is a PING frame.
   117  type debugFramePing struct{}
   118  
   119  func parseDebugFramePing(b []byte) (f debugFramePing, n int) {
   120  	return f, 1
   121  }
   122  
   123  func (f debugFramePing) String() string {
   124  	return "PING"
   125  }
   126  
   127  func (f debugFramePing) write(w *packetWriter) bool {
   128  	return w.appendPingFrame()
   129  }
   130  
   131  func (f debugFramePing) LogValue() slog.Value {
   132  	return slog.GroupValue(
   133  		slog.String("frame_type", "ping"),
   134  	)
   135  }
   136  
   137  // debugFrameAck is an ACK frame.
   138  type debugFrameAck struct {
   139  	ackDelay unscaledAckDelay
   140  	ranges   []i64range[packetNumber]
   141  }
   142  
   143  func parseDebugFrameAck(b []byte) (f debugFrameAck, n int) {
   144  	f.ranges = nil
   145  	_, f.ackDelay, n = consumeAckFrame(b, func(_ int, start, end packetNumber) {
   146  		f.ranges = append(f.ranges, i64range[packetNumber]{
   147  			start: start,
   148  			end:   end,
   149  		})
   150  	})
   151  	// Ranges are parsed high to low; reverse ranges slice to order them low to high.
   152  	for i := 0; i < len(f.ranges)/2; i++ {
   153  		j := len(f.ranges) - 1
   154  		f.ranges[i], f.ranges[j] = f.ranges[j], f.ranges[i]
   155  	}
   156  	return f, n
   157  }
   158  
   159  func (f debugFrameAck) String() string {
   160  	s := fmt.Sprintf("ACK Delay=%v", f.ackDelay)
   161  	for _, r := range f.ranges {
   162  		s += fmt.Sprintf(" [%v,%v)", r.start, r.end)
   163  	}
   164  	return s
   165  }
   166  
   167  func (f debugFrameAck) write(w *packetWriter) bool {
   168  	return w.appendAckFrame(rangeset[packetNumber](f.ranges), f.ackDelay)
   169  }
   170  
   171  func (f debugFrameAck) LogValue() slog.Value {
   172  	return slog.StringValue("error: debugFrameAck should not appear as a slog Value")
   173  }
   174  
   175  // debugFrameScaledAck is an ACK frame with scaled ACK Delay.
   176  //
   177  // This type is used in qlog events, which need access to the delay as a duration.
   178  type debugFrameScaledAck struct {
   179  	ackDelay time.Duration
   180  	ranges   []i64range[packetNumber]
   181  }
   182  
   183  func (f debugFrameScaledAck) LogValue() slog.Value {
   184  	var ackDelay slog.Attr
   185  	if f.ackDelay >= 0 {
   186  		ackDelay = slog.Duration("ack_delay", f.ackDelay)
   187  	}
   188  	return slog.GroupValue(
   189  		slog.String("frame_type", "ack"),
   190  		// Rather than trying to convert the ack ranges into the slog data model,
   191  		// pass a value that can JSON-encode itself.
   192  		slog.Any("acked_ranges", debugAckRanges(f.ranges)),
   193  		ackDelay,
   194  	)
   195  }
   196  
   197  type debugAckRanges []i64range[packetNumber]
   198  
   199  // AppendJSON appends a JSON encoding of the ack ranges to b, and returns it.
   200  // This is different than the standard json.Marshaler, but more efficient.
   201  // Since we only use this in cooperation with the qlog package,
   202  // encoding/json compatibility is irrelevant.
   203  func (r debugAckRanges) AppendJSON(b []byte) []byte {
   204  	b = append(b, '[')
   205  	for i, ar := range r {
   206  		start, end := ar.start, ar.end-1 // qlog ranges are closed-closed
   207  		if i != 0 {
   208  			b = append(b, ',')
   209  		}
   210  		b = append(b, '[')
   211  		b = strconv.AppendInt(b, int64(start), 10)
   212  		if start != end {
   213  			b = append(b, ',')
   214  			b = strconv.AppendInt(b, int64(end), 10)
   215  		}
   216  		b = append(b, ']')
   217  	}
   218  	b = append(b, ']')
   219  	return b
   220  }
   221  
   222  func (r debugAckRanges) String() string {
   223  	return string(r.AppendJSON(nil))
   224  }
   225  
   226  // debugFrameResetStream is a RESET_STREAM frame.
   227  type debugFrameResetStream struct {
   228  	id        streamID
   229  	code      uint64
   230  	finalSize int64
   231  }
   232  
   233  func parseDebugFrameResetStream(b []byte) (f debugFrameResetStream, n int) {
   234  	f.id, f.code, f.finalSize, n = consumeResetStreamFrame(b)
   235  	return f, n
   236  }
   237  
   238  func (f debugFrameResetStream) String() string {
   239  	return fmt.Sprintf("RESET_STREAM ID=%v Code=%v FinalSize=%v", f.id, f.code, f.finalSize)
   240  }
   241  
   242  func (f debugFrameResetStream) write(w *packetWriter) bool {
   243  	return w.appendResetStreamFrame(f.id, f.code, f.finalSize)
   244  }
   245  
   246  func (f debugFrameResetStream) LogValue() slog.Value {
   247  	return slog.GroupValue(
   248  		slog.String("frame_type", "reset_stream"),
   249  		slog.Uint64("stream_id", uint64(f.id)),
   250  		slog.Uint64("final_size", uint64(f.finalSize)),
   251  	)
   252  }
   253  
   254  // debugFrameStopSending is a STOP_SENDING frame.
   255  type debugFrameStopSending struct {
   256  	id   streamID
   257  	code uint64
   258  }
   259  
   260  func parseDebugFrameStopSending(b []byte) (f debugFrameStopSending, n int) {
   261  	f.id, f.code, n = consumeStopSendingFrame(b)
   262  	return f, n
   263  }
   264  
   265  func (f debugFrameStopSending) String() string {
   266  	return fmt.Sprintf("STOP_SENDING ID=%v Code=%v", f.id, f.code)
   267  }
   268  
   269  func (f debugFrameStopSending) write(w *packetWriter) bool {
   270  	return w.appendStopSendingFrame(f.id, f.code)
   271  }
   272  
   273  func (f debugFrameStopSending) LogValue() slog.Value {
   274  	return slog.GroupValue(
   275  		slog.String("frame_type", "stop_sending"),
   276  		slog.Uint64("stream_id", uint64(f.id)),
   277  		slog.Uint64("error_code", uint64(f.code)),
   278  	)
   279  }
   280  
   281  // debugFrameCrypto is a CRYPTO frame.
   282  type debugFrameCrypto struct {
   283  	off  int64
   284  	data []byte
   285  }
   286  
   287  func parseDebugFrameCrypto(b []byte) (f debugFrameCrypto, n int) {
   288  	f.off, f.data, n = consumeCryptoFrame(b)
   289  	return f, n
   290  }
   291  
   292  func (f debugFrameCrypto) String() string {
   293  	return fmt.Sprintf("CRYPTO Offset=%v Length=%v", f.off, len(f.data))
   294  }
   295  
   296  func (f debugFrameCrypto) write(w *packetWriter) bool {
   297  	b, added := w.appendCryptoFrame(f.off, len(f.data))
   298  	copy(b, f.data)
   299  	return added
   300  }
   301  
   302  func (f debugFrameCrypto) LogValue() slog.Value {
   303  	return slog.GroupValue(
   304  		slog.String("frame_type", "crypto"),
   305  		slog.Int64("offset", f.off),
   306  		slog.Int("length", len(f.data)),
   307  	)
   308  }
   309  
   310  // debugFrameNewToken is a NEW_TOKEN frame.
   311  type debugFrameNewToken struct {
   312  	token []byte
   313  }
   314  
   315  func parseDebugFrameNewToken(b []byte) (f debugFrameNewToken, n int) {
   316  	f.token, n = consumeNewTokenFrame(b)
   317  	return f, n
   318  }
   319  
   320  func (f debugFrameNewToken) String() string {
   321  	return fmt.Sprintf("NEW_TOKEN Token=%x", f.token)
   322  }
   323  
   324  func (f debugFrameNewToken) write(w *packetWriter) bool {
   325  	return w.appendNewTokenFrame(f.token)
   326  }
   327  
   328  func (f debugFrameNewToken) LogValue() slog.Value {
   329  	return slog.GroupValue(
   330  		slog.String("frame_type", "new_token"),
   331  		slogHexstring("token", f.token),
   332  	)
   333  }
   334  
   335  // debugFrameStream is a STREAM frame.
   336  type debugFrameStream struct {
   337  	id   streamID
   338  	fin  bool
   339  	off  int64
   340  	data []byte
   341  }
   342  
   343  func parseDebugFrameStream(b []byte) (f debugFrameStream, n int) {
   344  	f.id, f.off, f.fin, f.data, n = consumeStreamFrame(b)
   345  	return f, n
   346  }
   347  
   348  func (f debugFrameStream) String() string {
   349  	fin := ""
   350  	if f.fin {
   351  		fin = " FIN"
   352  	}
   353  	return fmt.Sprintf("STREAM ID=%v%v Offset=%v Length=%v", f.id, fin, f.off, len(f.data))
   354  }
   355  
   356  func (f debugFrameStream) write(w *packetWriter) bool {
   357  	b, added := w.appendStreamFrame(f.id, f.off, len(f.data), f.fin)
   358  	copy(b, f.data)
   359  	return added
   360  }
   361  
   362  func (f debugFrameStream) LogValue() slog.Value {
   363  	var fin slog.Attr
   364  	if f.fin {
   365  		fin = slog.Bool("fin", true)
   366  	}
   367  	return slog.GroupValue(
   368  		slog.String("frame_type", "stream"),
   369  		slog.Uint64("stream_id", uint64(f.id)),
   370  		slog.Int64("offset", f.off),
   371  		slog.Int("length", len(f.data)),
   372  		fin,
   373  	)
   374  }
   375  
   376  // debugFrameMaxData is a MAX_DATA frame.
   377  type debugFrameMaxData struct {
   378  	max int64
   379  }
   380  
   381  func parseDebugFrameMaxData(b []byte) (f debugFrameMaxData, n int) {
   382  	f.max, n = consumeMaxDataFrame(b)
   383  	return f, n
   384  }
   385  
   386  func (f debugFrameMaxData) String() string {
   387  	return fmt.Sprintf("MAX_DATA Max=%v", f.max)
   388  }
   389  
   390  func (f debugFrameMaxData) write(w *packetWriter) bool {
   391  	return w.appendMaxDataFrame(f.max)
   392  }
   393  
   394  func (f debugFrameMaxData) LogValue() slog.Value {
   395  	return slog.GroupValue(
   396  		slog.String("frame_type", "max_data"),
   397  		slog.Int64("maximum", f.max),
   398  	)
   399  }
   400  
   401  // debugFrameMaxStreamData is a MAX_STREAM_DATA frame.
   402  type debugFrameMaxStreamData struct {
   403  	id  streamID
   404  	max int64
   405  }
   406  
   407  func parseDebugFrameMaxStreamData(b []byte) (f debugFrameMaxStreamData, n int) {
   408  	f.id, f.max, n = consumeMaxStreamDataFrame(b)
   409  	return f, n
   410  }
   411  
   412  func (f debugFrameMaxStreamData) String() string {
   413  	return fmt.Sprintf("MAX_STREAM_DATA ID=%v Max=%v", f.id, f.max)
   414  }
   415  
   416  func (f debugFrameMaxStreamData) write(w *packetWriter) bool {
   417  	return w.appendMaxStreamDataFrame(f.id, f.max)
   418  }
   419  
   420  func (f debugFrameMaxStreamData) LogValue() slog.Value {
   421  	return slog.GroupValue(
   422  		slog.String("frame_type", "max_stream_data"),
   423  		slog.Uint64("stream_id", uint64(f.id)),
   424  		slog.Int64("maximum", f.max),
   425  	)
   426  }
   427  
   428  // debugFrameMaxStreams is a MAX_STREAMS frame.
   429  type debugFrameMaxStreams struct {
   430  	streamType streamType
   431  	max        int64
   432  }
   433  
   434  func parseDebugFrameMaxStreams(b []byte) (f debugFrameMaxStreams, n int) {
   435  	f.streamType, f.max, n = consumeMaxStreamsFrame(b)
   436  	return f, n
   437  }
   438  
   439  func (f debugFrameMaxStreams) String() string {
   440  	return fmt.Sprintf("MAX_STREAMS Type=%v Max=%v", f.streamType, f.max)
   441  }
   442  
   443  func (f debugFrameMaxStreams) write(w *packetWriter) bool {
   444  	return w.appendMaxStreamsFrame(f.streamType, f.max)
   445  }
   446  
   447  func (f debugFrameMaxStreams) LogValue() slog.Value {
   448  	return slog.GroupValue(
   449  		slog.String("frame_type", "max_streams"),
   450  		slog.String("stream_type", f.streamType.qlogString()),
   451  		slog.Int64("maximum", f.max),
   452  	)
   453  }
   454  
   455  // debugFrameDataBlocked is a DATA_BLOCKED frame.
   456  type debugFrameDataBlocked struct {
   457  	max int64
   458  }
   459  
   460  func parseDebugFrameDataBlocked(b []byte) (f debugFrameDataBlocked, n int) {
   461  	f.max, n = consumeDataBlockedFrame(b)
   462  	return f, n
   463  }
   464  
   465  func (f debugFrameDataBlocked) String() string {
   466  	return fmt.Sprintf("DATA_BLOCKED Max=%v", f.max)
   467  }
   468  
   469  func (f debugFrameDataBlocked) write(w *packetWriter) bool {
   470  	return w.appendDataBlockedFrame(f.max)
   471  }
   472  
   473  func (f debugFrameDataBlocked) LogValue() slog.Value {
   474  	return slog.GroupValue(
   475  		slog.String("frame_type", "data_blocked"),
   476  		slog.Int64("limit", f.max),
   477  	)
   478  }
   479  
   480  // debugFrameStreamDataBlocked is a STREAM_DATA_BLOCKED frame.
   481  type debugFrameStreamDataBlocked struct {
   482  	id  streamID
   483  	max int64
   484  }
   485  
   486  func parseDebugFrameStreamDataBlocked(b []byte) (f debugFrameStreamDataBlocked, n int) {
   487  	f.id, f.max, n = consumeStreamDataBlockedFrame(b)
   488  	return f, n
   489  }
   490  
   491  func (f debugFrameStreamDataBlocked) String() string {
   492  	return fmt.Sprintf("STREAM_DATA_BLOCKED ID=%v Max=%v", f.id, f.max)
   493  }
   494  
   495  func (f debugFrameStreamDataBlocked) write(w *packetWriter) bool {
   496  	return w.appendStreamDataBlockedFrame(f.id, f.max)
   497  }
   498  
   499  func (f debugFrameStreamDataBlocked) LogValue() slog.Value {
   500  	return slog.GroupValue(
   501  		slog.String("frame_type", "stream_data_blocked"),
   502  		slog.Uint64("stream_id", uint64(f.id)),
   503  		slog.Int64("limit", f.max),
   504  	)
   505  }
   506  
   507  // debugFrameStreamsBlocked is a STREAMS_BLOCKED frame.
   508  type debugFrameStreamsBlocked struct {
   509  	streamType streamType
   510  	max        int64
   511  }
   512  
   513  func parseDebugFrameStreamsBlocked(b []byte) (f debugFrameStreamsBlocked, n int) {
   514  	f.streamType, f.max, n = consumeStreamsBlockedFrame(b)
   515  	return f, n
   516  }
   517  
   518  func (f debugFrameStreamsBlocked) String() string {
   519  	return fmt.Sprintf("STREAMS_BLOCKED Type=%v Max=%v", f.streamType, f.max)
   520  }
   521  
   522  func (f debugFrameStreamsBlocked) write(w *packetWriter) bool {
   523  	return w.appendStreamsBlockedFrame(f.streamType, f.max)
   524  }
   525  
   526  func (f debugFrameStreamsBlocked) LogValue() slog.Value {
   527  	return slog.GroupValue(
   528  		slog.String("frame_type", "streams_blocked"),
   529  		slog.String("stream_type", f.streamType.qlogString()),
   530  		slog.Int64("limit", f.max),
   531  	)
   532  }
   533  
   534  // debugFrameNewConnectionID is a NEW_CONNECTION_ID frame.
   535  type debugFrameNewConnectionID struct {
   536  	seq           int64
   537  	retirePriorTo int64
   538  	connID        []byte
   539  	token         statelessResetToken
   540  }
   541  
   542  func parseDebugFrameNewConnectionID(b []byte) (f debugFrameNewConnectionID, n int) {
   543  	f.seq, f.retirePriorTo, f.connID, f.token, n = consumeNewConnectionIDFrame(b)
   544  	return f, n
   545  }
   546  
   547  func (f debugFrameNewConnectionID) String() string {
   548  	return fmt.Sprintf("NEW_CONNECTION_ID Seq=%v Retire=%v ID=%x Token=%x", f.seq, f.retirePriorTo, f.connID, f.token[:])
   549  }
   550  
   551  func (f debugFrameNewConnectionID) write(w *packetWriter) bool {
   552  	return w.appendNewConnectionIDFrame(f.seq, f.retirePriorTo, f.connID, f.token)
   553  }
   554  
   555  func (f debugFrameNewConnectionID) LogValue() slog.Value {
   556  	return slog.GroupValue(
   557  		slog.String("frame_type", "new_connection_id"),
   558  		slog.Int64("sequence_number", f.seq),
   559  		slog.Int64("retire_prior_to", f.retirePriorTo),
   560  		slogHexstring("connection_id", f.connID),
   561  		slogHexstring("stateless_reset_token", f.token[:]),
   562  	)
   563  }
   564  
   565  // debugFrameRetireConnectionID is a NEW_CONNECTION_ID frame.
   566  type debugFrameRetireConnectionID struct {
   567  	seq int64
   568  }
   569  
   570  func parseDebugFrameRetireConnectionID(b []byte) (f debugFrameRetireConnectionID, n int) {
   571  	f.seq, n = consumeRetireConnectionIDFrame(b)
   572  	return f, n
   573  }
   574  
   575  func (f debugFrameRetireConnectionID) String() string {
   576  	return fmt.Sprintf("RETIRE_CONNECTION_ID Seq=%v", f.seq)
   577  }
   578  
   579  func (f debugFrameRetireConnectionID) write(w *packetWriter) bool {
   580  	return w.appendRetireConnectionIDFrame(f.seq)
   581  }
   582  
   583  func (f debugFrameRetireConnectionID) LogValue() slog.Value {
   584  	return slog.GroupValue(
   585  		slog.String("frame_type", "retire_connection_id"),
   586  		slog.Int64("sequence_number", f.seq),
   587  	)
   588  }
   589  
   590  // debugFramePathChallenge is a PATH_CHALLENGE frame.
   591  type debugFramePathChallenge struct {
   592  	data pathChallengeData
   593  }
   594  
   595  func parseDebugFramePathChallenge(b []byte) (f debugFramePathChallenge, n int) {
   596  	f.data, n = consumePathChallengeFrame(b)
   597  	return f, n
   598  }
   599  
   600  func (f debugFramePathChallenge) String() string {
   601  	return fmt.Sprintf("PATH_CHALLENGE Data=%x", f.data)
   602  }
   603  
   604  func (f debugFramePathChallenge) write(w *packetWriter) bool {
   605  	return w.appendPathChallengeFrame(f.data)
   606  }
   607  
   608  func (f debugFramePathChallenge) LogValue() slog.Value {
   609  	return slog.GroupValue(
   610  		slog.String("frame_type", "path_challenge"),
   611  		slog.String("data", fmt.Sprintf("%x", f.data)),
   612  	)
   613  }
   614  
   615  // debugFramePathResponse is a PATH_RESPONSE frame.
   616  type debugFramePathResponse struct {
   617  	data pathChallengeData
   618  }
   619  
   620  func parseDebugFramePathResponse(b []byte) (f debugFramePathResponse, n int) {
   621  	f.data, n = consumePathResponseFrame(b)
   622  	return f, n
   623  }
   624  
   625  func (f debugFramePathResponse) String() string {
   626  	return fmt.Sprintf("PATH_RESPONSE Data=%x", f.data)
   627  }
   628  
   629  func (f debugFramePathResponse) write(w *packetWriter) bool {
   630  	return w.appendPathResponseFrame(f.data)
   631  }
   632  
   633  func (f debugFramePathResponse) LogValue() slog.Value {
   634  	return slog.GroupValue(
   635  		slog.String("frame_type", "path_response"),
   636  		slog.String("data", fmt.Sprintf("%x", f.data)),
   637  	)
   638  }
   639  
   640  // debugFrameConnectionCloseTransport is a CONNECTION_CLOSE frame carrying a transport error.
   641  type debugFrameConnectionCloseTransport struct {
   642  	code      transportError
   643  	frameType uint64
   644  	reason    string
   645  }
   646  
   647  func parseDebugFrameConnectionCloseTransport(b []byte) (f debugFrameConnectionCloseTransport, n int) {
   648  	f.code, f.frameType, f.reason, n = consumeConnectionCloseTransportFrame(b)
   649  	return f, n
   650  }
   651  
   652  func (f debugFrameConnectionCloseTransport) String() string {
   653  	s := fmt.Sprintf("CONNECTION_CLOSE Code=%v", f.code)
   654  	if f.frameType != 0 {
   655  		s += fmt.Sprintf(" FrameType=%v", f.frameType)
   656  	}
   657  	if f.reason != "" {
   658  		s += fmt.Sprintf(" Reason=%q", f.reason)
   659  	}
   660  	return s
   661  }
   662  
   663  func (f debugFrameConnectionCloseTransport) write(w *packetWriter) bool {
   664  	return w.appendConnectionCloseTransportFrame(f.code, f.frameType, f.reason)
   665  }
   666  
   667  func (f debugFrameConnectionCloseTransport) LogValue() slog.Value {
   668  	return slog.GroupValue(
   669  		slog.String("frame_type", "connection_close"),
   670  		slog.String("error_space", "transport"),
   671  		slog.Uint64("error_code_value", uint64(f.code)),
   672  		slog.String("reason", f.reason),
   673  	)
   674  }
   675  
   676  // debugFrameConnectionCloseApplication is a CONNECTION_CLOSE frame carrying an application error.
   677  type debugFrameConnectionCloseApplication struct {
   678  	code   uint64
   679  	reason string
   680  }
   681  
   682  func parseDebugFrameConnectionCloseApplication(b []byte) (f debugFrameConnectionCloseApplication, n int) {
   683  	f.code, f.reason, n = consumeConnectionCloseApplicationFrame(b)
   684  	return f, n
   685  }
   686  
   687  func (f debugFrameConnectionCloseApplication) String() string {
   688  	s := fmt.Sprintf("CONNECTION_CLOSE AppCode=%v", f.code)
   689  	if f.reason != "" {
   690  		s += fmt.Sprintf(" Reason=%q", f.reason)
   691  	}
   692  	return s
   693  }
   694  
   695  func (f debugFrameConnectionCloseApplication) write(w *packetWriter) bool {
   696  	return w.appendConnectionCloseApplicationFrame(f.code, f.reason)
   697  }
   698  
   699  func (f debugFrameConnectionCloseApplication) LogValue() slog.Value {
   700  	return slog.GroupValue(
   701  		slog.String("frame_type", "connection_close"),
   702  		slog.String("error_space", "application"),
   703  		slog.Uint64("error_code_value", uint64(f.code)),
   704  		slog.String("reason", f.reason),
   705  	)
   706  }
   707  
   708  // debugFrameHandshakeDone is a HANDSHAKE_DONE frame.
   709  type debugFrameHandshakeDone struct{}
   710  
   711  func parseDebugFrameHandshakeDone(b []byte) (f debugFrameHandshakeDone, n int) {
   712  	return f, 1
   713  }
   714  
   715  func (f debugFrameHandshakeDone) String() string {
   716  	return "HANDSHAKE_DONE"
   717  }
   718  
   719  func (f debugFrameHandshakeDone) write(w *packetWriter) bool {
   720  	return w.appendHandshakeDoneFrame()
   721  }
   722  
   723  func (f debugFrameHandshakeDone) LogValue() slog.Value {
   724  	return slog.GroupValue(
   725  		slog.String("frame_type", "handshake_done"),
   726  	)
   727  }