Files
agent-radar 2ed3415fb7
Publish Claude Code Binaries / Stage and publish binaries (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-arm64 (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build linux-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build windows-amd64 (push) Canceled after 0s
Sync Tutti Agent Skills / sync (push) Canceled after 0s
Windows Agent Adapters / Test Windows agent adapters (push) Canceled after 0s
Windows Daemon Adapters / Test Windows daemon adapters (push) Canceled after 0s
Publish Tutti App Runtime / Publish runtime catalog (push) Canceled after 0s
源码快照 @ 2026-09-01 (source-only, 剔除非源码素材)
2026-09-01 20:17:27 +08:00

191 lines
5.2 KiB
Go

package devicelink
import (
"context"
"crypto/rand"
"errors"
"fmt"
"io"
"net"
"sync"
"time"
)
const (
streamProbeRequest = "tutti-device-link/stream-probe/1"
streamProbeAck = "tutti-device-link/stream-probe-ack/1"
streamProbeNonce = 16
streamProbeLimit = 5 * time.Second
)
var (
ErrStreamProbeRejected = errors.New("device-link stream probe rejected")
ErrStreamProbeMismatch = errors.New("device-link stream probe acknowledgement mismatch")
)
// ProbeStream verifies that the authenticated peer can receive and answer a
// fresh stream before the caller commits a direct/Relay race winner. QUIC
// stream allocation alone is not a liveness signal: a stale session may still
// allocate a local stream after its network path has become unusable.
//
// The probe is transport-owned and carries no Agent or product payload. The
// peer must run ServeStreamProbe before handing the stream to its application
// protocol handler. The same stream is left open after the acknowledgement so
// callers can immediately write their application prelude without a second
// race or an ambiguous request retry.
func ProbeStream(ctx context.Context, stream net.Conn) error {
if stream == nil {
return errors.New("device-link stream probe requires a stream")
}
probeCtx, cancel := streamProbeContext(ctx)
defer cancel()
stopClose := closeStreamOnContext(probeCtx, stream)
defer stopClose()
if err := setProbeDeadline(probeCtx, stream); err != nil {
return fmt.Errorf("set device-link stream probe deadline: %w", err)
}
defer func() { _ = stream.SetDeadline(time.Time{}) }()
nonce := make([]byte, streamProbeNonce)
if _, err := io.ReadFull(rand.Reader, nonce); err != nil {
return fmt.Errorf("generate device-link stream probe nonce: %w", err)
}
if err := writeProbeMessage(stream, streamProbeRequest, nonce); err != nil {
return fmt.Errorf("write device-link stream probe: %w", err)
}
ack, err := readProbeMessage(stream, streamProbeAck)
if err != nil {
return err
}
if !equalBytes(ack, nonce) {
return ErrStreamProbeMismatch
}
stopClose()
cancel()
_ = stream.SetDeadline(time.Time{})
return nil
}
// ServeStreamProbe acknowledges one ProbeStream call and then invokes next
// with the same stream. A failed probe never reaches the product handler.
// This keeps application framing and request semantics out of the shared
// transport handshake while still proving that the remote endpoint is alive.
func ServeStreamProbe(
ctx context.Context,
stream net.Conn,
next func(context.Context, net.Conn) error,
) error {
if stream == nil {
return errors.New("device-link stream probe requires a stream")
}
if next == nil {
return errors.New("device-link stream probe handler is required")
}
probeCtx, cancel := streamProbeContext(ctx)
defer cancel()
stopClose := closeStreamOnContext(probeCtx, stream)
defer stopClose()
if err := setProbeDeadline(probeCtx, stream); err != nil {
return fmt.Errorf("set device-link stream probe deadline: %w", err)
}
defer func() { _ = stream.SetDeadline(time.Time{}) }()
nonce, err := readProbeMessage(stream, streamProbeRequest)
if err != nil {
return err
}
if err := writeProbeMessage(stream, streamProbeAck, nonce); err != nil {
return fmt.Errorf("write device-link stream probe acknowledgement: %w", err)
}
stopClose()
cancel()
_ = stream.SetDeadline(time.Time{})
return next(ctx, stream)
}
func streamProbeContext(ctx context.Context) (context.Context, context.CancelFunc) {
if ctx == nil {
ctx = context.Background()
}
if _, ok := ctx.Deadline(); ok {
return ctx, func() {}
}
return context.WithTimeout(ctx, streamProbeLimit)
}
func closeStreamOnContext(ctx context.Context, stream net.Conn) func() {
done := make(chan struct{})
stopped := make(chan struct{})
var once sync.Once
go func() {
defer close(stopped)
select {
case <-ctx.Done():
_ = stream.Close()
case <-done:
}
}()
return func() {
once.Do(func() {
close(done)
<-stopped
})
}
}
func setProbeDeadline(ctx context.Context, stream net.Conn) error {
deadline, ok := ctx.Deadline()
if !ok {
return nil
}
return stream.SetDeadline(deadline)
}
func writeProbeMessage(writer io.Writer, kind string, nonce []byte) error {
if err := writeProbeBytes(writer, []byte(kind)); err != nil {
return err
}
return writeProbeBytes(writer, nonce)
}
func readProbeMessage(reader io.Reader, kind string) ([]byte, error) {
header := make([]byte, len(kind))
if _, err := io.ReadFull(reader, header); err != nil {
return nil, fmt.Errorf("read device-link stream probe header: %w", err)
}
if string(header) != kind {
return nil, ErrStreamProbeRejected
}
nonce := make([]byte, streamProbeNonce)
if _, err := io.ReadFull(reader, nonce); err != nil {
return nil, fmt.Errorf("read device-link stream probe nonce: %w", err)
}
return nonce, nil
}
func writeProbeBytes(writer io.Writer, payload []byte) error {
for len(payload) > 0 {
written, err := writer.Write(payload)
if err != nil {
return err
}
if written <= 0 || written > len(payload) {
return io.ErrShortWrite
}
payload = payload[written:]
}
return nil
}
func equalBytes(left, right []byte) bool {
if len(left) != len(right) {
return false
}
for index := range left {
if left[index] != right[index] {
return false
}
}
return true
}