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 }