startKeepalive starts the keepalive mechanism for a session. It assigns the cancel function to the provided cancelPtr and starts a goroutine that sends ping messages at the specified interval. logger must be non-nil; ping failures (which terminate the keepalive loop and close the session) are repor
(session keepaliveSession, interval time.Duration, cancelPtr *context.CancelFunc, logger *slog.Logger)
| 591 | // logger must be non-nil; ping failures (which terminate the keepalive loop and |
| 592 | // close the session) are reported via logger so they are not silently dropped. |
| 593 | func startKeepalive(session keepaliveSession, interval time.Duration, cancelPtr *context.CancelFunc, logger *slog.Logger) { |
| 594 | ctx, cancel := context.WithCancel(context.Background()) |
| 595 | // Assign cancel function before starting goroutine to avoid race condition. |
| 596 | // We cannot return it because the caller may need to cancel during the |
| 597 | // window between goroutine scheduling and function return. |
| 598 | *cancelPtr = cancel |
| 599 | |
| 600 | go func() { |
| 601 | ticker := time.NewTicker(interval) |
| 602 | defer ticker.Stop() |
| 603 | |
| 604 | for { |
| 605 | select { |
| 606 | case <-ctx.Done(): |
| 607 | return |
| 608 | case <-ticker.C: |
| 609 | pingCtx, pingCancel := context.WithTimeout(context.Background(), interval/2) |
| 610 | err := session.Ping(pingCtx, nil) |
| 611 | pingCancel() |
| 612 | if err != nil { |
| 613 | if errors.Is(err, jsonrpc2.ErrMethodNotFound) { |
| 614 | // Peer doesn't support ping, stop the keepalive process. |
| 615 | return |
| 616 | } |
| 617 | // Ping failed; log it before closing the session so the |
| 618 | // failure is observable to operators. See #218. |
| 619 | logger.Error("keepalive ping failed; closing session", "error", err) |
| 620 | _ = session.Close() |
| 621 | return |
| 622 | } |
| 623 | } |
| 624 | } |
| 625 | }() |
| 626 | } |
no test coverage detected
searching dependent graphs…