Files
zeekay 28cfc05ec5 zapclient: fix native-TCP Client→Server path (opcode routing + static dial)
Two defects made the canonical zapclient native-TCP path non-functional
(undetected — no test did a real Client→Server TCP round-trip; zapweb
works via Dispatch, inter-shard uses raw zap.Node):

1. Server.Register bound the node handler under the full opcode, but the
   Node routes inbound by Flags()>>8 (the high byte). Mismatch → handler
   never found → call hung. Register now uses op>>8; dispatch still reads
   the full Flags() to select the procedure.
2. Connect with a static Discovery set the node NoDiscovery, so it never
   learned peer addresses and could not dial. Added WithStaticPeers + a
   static Discovery, ConnectDirectID (returns the handshake-learned
   NodeID), and client-side dial-by-address (memoized) so static peers
   (placeholder NodeID + real Address — the cloud/K8s Service-DNS path)
   are reachable. mDNS path unchanged.

Adds TestNativeTCPRoundTrip as the regression guard.
2026-06-15 23:36:04 -07:00

59 lines
1.5 KiB
Go

// static.go — a fixed peer-address Discovery, bypassing mDNS.
//
// This is what WithStaticPeers builds: the cloud/K8s path where peers
// are reached by a known address (a Service DNS name). The NodeID is a
// placeholder for observability — the real peer identity is learned at
// dial time (Node.ConnectDirectID) and authenticated by the mTLS peer
// cert, so the client addresses Calls by the learned id, not this one.
package zapclient
import (
"strconv"
"sync"
"time"
)
type staticDiscovery struct {
serviceType string
mu sync.RWMutex
peers []Peer
}
func newStaticDiscovery(serviceType string, addrs []string) *staticDiscovery {
now := time.Now()
peers := make([]Peer, 0, len(addrs))
for i, addr := range addrs {
if addr == "" {
continue
}
peers = append(peers, Peer{
NodeID: serviceType + "-static-" + strconv.Itoa(i),
ServiceType: serviceType,
Address: addr,
LastSeen: now,
})
}
return &staticDiscovery{serviceType: serviceType, peers: peers}
}
func (s *staticDiscovery) Peers() []Peer {
s.mu.RLock()
defer s.mu.RUnlock()
out := make([]Peer, len(s.peers))
copy(out, s.peers)
return out
}
func (s *staticDiscovery) PeerCount() int {
s.mu.RLock()
defer s.mu.RUnlock()
return len(s.peers)
}
func (s *staticDiscovery) ServiceType() string { return s.serviceType }
func (s *staticDiscovery) Start() error { return nil }
func (s *staticDiscovery) Stop() {}
var _ Discovery = (*staticDiscovery)(nil)