package socketimport ()// Path-state pruning limits. These bound the number of candidate paths an actor// keeps per remote so the set cannot grow without limit. Relay paths are never// counted or pruned. Values match the Rust reference// (iroh/src/socket/remote_map/remote_state/path_state.rs:18,23).const (// MaxNonRelayPaths is the maximum number of non-relay paths kept per remote.MaxNonRelayPaths = 30// MaxInactiveNonRelayPaths is the maximum number of inactive (previously // open, now closed) non-relay paths kept per remote.MaxInactiveNonRelayPaths = 10)// PathStatus is the lifecycle status of a candidate path. It mirrors the Rust// PathStatus enum (path_state.rs:44).typePathStatusintconst (// PathStatusUnknown is a path that has never been dialed: it was added by an // address-lookup mechanism and is only potentially usable.PathStatusUnknownPathStatus = iota// PathStatusOpen is a path that is currently open in QUIC.PathStatusOpen// PathStatusInactive is a path that was open at some point but has since // closed. The time records when it closed, used to prune oldest-first.PathStatusInactive// PathStatusUnusable is a path where hole-punching was attempted and failed.PathStatusUnusable)func ( PathStatus) () string {switch {casePathStatusUnknown:return"unknown"casePathStatusOpen:return"open"casePathStatusInactive:return"inactive"casePathStatusUnusable:return"unusable"default:return"invalid" }}// PathState is the per-path bookkeeping kept by [RemotePathState].typePathStatestruct {// Status is the current lifecycle status of the path. Status PathStatus// lastActive records when an open path was last observed as active. lastActive time.Time// closedAt records when an inactive path was last closed; it is only // meaningful when Status is PathStatusInactive and orders inactive-path // pruning (most recently closed kept first). closedAt time.Time}// TransportAddrUsage reports whether a remote transport address is currently// active.typeTransportAddrUsageintconst (// TransportAddrInactive means the address is known but not currently used.TransportAddrInactiveTransportAddrUsage = iota// TransportAddrActive means the address is currently used.TransportAddrActive)// TransportAddrInfo is a remote transport address plus usage metadata.typeTransportAddrInfostruct { Addr netaddr.TransportAddr Usage TransportAddrUsage Provenance string}// RemotePathState tracks all candidate paths to a single remote endpoint:// direct IP, relay, and custom transport addresses, each with a [PathStatus].// It is the Go analog of the Rust RemotePathState (path_state.rs).//// Paths added by address lookup start [PathStatusUnknown]; QUIC path events move// them through Open and Inactive; failed hole-punches mark them Unusable. The// set is bounded by [RemotePathState.Prune], which keeps at most// [MaxNonRelayPaths] non-relay paths plus [MaxInactiveNonRelayPaths] inactive// non-relay paths. Relay paths are never pruned.//// RemotePathState is not safe for concurrent use; it is owned by a single// [RemoteStateActor] goroutine.typeRemotePathStatestruct { paths map[string]pathEntry}// pathEntry stores a path's [Addr] alongside its [PathState]. The map is keyed// by Addr.String() because [Addr] is not directly comparable (it embeds a// non-comparable netaddr.CustomAddr).type pathEntry struct { addr Addr state PathState provenance string}// NewRemotePathState returns an empty path-state tracker.func () *RemotePathState {return &RemotePathState{paths: make(map[string]pathEntry)}}// key returns the stable map key for an [Addr].func pathKey( Addr) string { return .String() }// IsEmpty reports whether no paths are known.func ( *RemotePathState) () bool { returnlen(.paths) == 0 }// Len returns the number of known paths, including relay paths.func ( *RemotePathState) () int { returnlen(.paths) }// Addrs returns the addresses of all known paths in unspecified order.func ( *RemotePathState) () []Addr { := make([]Addr, 0, len(.paths))for , := range .paths { = append(, .addr) }return}// OpenAddrs returns the addresses of all currently open paths.func ( *RemotePathState) () []Addr { := make([]Addr, 0, len(.paths))for , := range .paths {if .state.Status == PathStatusOpen { = append(, .addr) } }sort.Slice(, func(, int) bool {return [].String() < [].String() })return}// RemoteAddrs returns all known remote addresses with active/inactive usage.func ( *RemotePathState) () []TransportAddrInfo { := make([]TransportAddrInfo, 0, len(.paths))for , := range .paths { , := transportAddrFromAddr(.addr)if ! {continue } := TransportAddrInactiveif .state.Status == PathStatusOpen { = TransportAddrActive } = append(, TransportAddrInfo{Addr: , Usage: , Provenance: .provenance}) }sort.Slice(, func(, int) bool {return [].Addr.String() < [].Addr.String() })return}// Status returns the status of addr and whether it is known.func ( *RemotePathState) ( Addr) (PathStatus, bool) { , := .paths[pathKey()]if ! {returnPathStatusUnknown, false }return .state.Status, true}// Add records a candidate path with [PathStatusUnknown] if it is not already// known. A path already present keeps its current status.func ( *RemotePathState) ( Addr) { .AddWithProvenance(, "")}// AddWithProvenance records a candidate path with lookup provenance.func ( *RemotePathState) ( Addr, string) { := pathKey()if , := .paths[]; {if .provenance == "" && != "" { .provenance = .paths[] = }return } .paths[] = pathEntry{addr: , state: PathState{Status: PathStatusUnknown}, provenance: }}// SetOpen marks addr as open, adding it if unknown. It mirrors the Rust// add_path / on path-open transition (path_state.rs:90).func ( *RemotePathState) ( Addr) { .SetOpenAt(, time.Now())}// SetOpenAt marks addr as open with an explicit activity time. It is used by// tests and by the actor heartbeat, which already has a shared timestamp for// all observed paths.func ( *RemotePathState) ( Addr, time.Time) { := pathKey() := .paths[] .addr = .state = PathState{Status: PathStatusOpen, lastActive: } .paths[] = }// SetClosed transitions addr toward an inactive/unusable status, recording the// close time for inactive pruning. It mirrors the Rust remove_path transition// (path_state.rs:106): an open or already-inactive path becomes inactive (still// considered usable later); an unusable or unknown path becomes unusable.func ( *RemotePathState) ( Addr, time.Time) { := pathKey() , := .paths[]if ! {return }switch .state.Status {casePathStatusOpen, PathStatusInactive: .state.Status = PathStatusInactive .state.closedAt = casePathStatusUnusable, PathStatusUnknown: .state.Status = PathStatusUnusable } .paths[] = }// SetUnusable marks addr unusable: a hole-punch was attempted and failed.func ( *RemotePathState) ( Addr) { := pathKey() , := .paths[]if ! { = pathEntry{addr: } } .addr = .state.Status = PathStatusUnusable .paths[] = }// ExpireIdle closes open paths that have not been observed within their path// idle timeout. Direct and custom paths use [PathMaxIdleTimeout]; relay paths// use [RelayPathMaxIdleTimeout].func ( *RemotePathState) ( time.Time) []Addr {var []Addrfor , := range .paths {if .state.Status != PathStatusOpen || .state.lastActive.IsZero() {continue } := PathMaxIdleTimeoutifisRelayAddr(.addr) { = RelayPathMaxIdleTimeout }if .Sub(.state.lastActive) < {continue } .state.Status = PathStatusInactive .state.closedAt = .paths[] = = append(, .addr) }return}// Prune bounds the non-relay path set. It is a no-op when there are fewer than// [MaxNonRelayPaths] non-relay paths. Otherwise it removes failed (unusable)// paths and all but the [MaxInactiveNonRelayPaths] most-recently-closed inactive// paths. Open and unknown paths are always kept; relay paths are never pruned or// counted. It mirrors prune_non_relay_paths (path_state.rs:254).func ( *RemotePathState) () {// Bail early if the total path count is below the limit.iflen(.paths) < MaxNonRelayPaths {return } := 0for , := range .paths {if !isRelayAddr(.addr) { ++ } }if < MaxNonRelayPaths {return }typestruct {stringtime.Time }var []var []stringfor , := range .paths {ifisRelayAddr(.addr) {continue }switch .state.Status {casePathStatusInactive: = append(, {: , : .state.closedAt})casePathStatusUnusable: = append(, ) } }// If every path failed, do not prune all of them: keep MaxNonRelayPaths. // This implies inactive is empty.iflen() == len(.paths) { := len(.paths) - MaxNonRelayPathsif < 0 { = 0 } = [:] }// Sort inactive most-recently-closed first, then drop everything beyond the // MaxInactiveNonRelayPaths we keep.sort.Slice(, func(, int) bool {return []..After([].) }) := MaxInactiveNonRelayPathsif > len() { = len() } := [:] := make(map[string]struct{}, len()+len())for , := range { [] = struct{}{} }for , := range { [.] = struct{}{} }for := range {delete(.paths, ) }}// isRelayAddr reports whether a is a relay path.func isRelayAddr( Addr) bool { return .Kind() == AddrRelay }func transportAddrFromAddr( Addr) (netaddr.TransportAddr, bool) {if , := .IP(); {returnnetaddr.IPAddr{Addr: }, true }if , , := .Relay(); {returnnetaddr.RelayAddr{URL: }, true }if , := .Custom(); {return , true }returnnil, false}
The pages are generated with Goldsv0.8.4. (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu.
PR and bug reports are welcome and can be submitted to the issue list.
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds.