feat: remove local multi-hop test harness and update Makefile and server configurations

Kim committed May 14, 2026 at 22:59 UTC d71640c43b2400c2377ed2db0d3eef84f9e7cf57
5 files changed +10 -532
Makefile
+2 -6
@@ -1,4 +1,4 @@
1 -.PHONY: help install fmt vet lint lint-auto test test-local-multihop vuln tidy all run build build-frontend build-docs build-tunnel build-server clean load-test
1 +.PHONY: help install fmt vet lint lint-auto test vuln tidy all run build build-frontend build-docs build-tunnel build-server clean load-test
2
3 .DEFAULT_GOAL := help
4
@@ -15,8 +15,7 @@ help:
15 @echo " make install - Install Go developer tools used by this repo"
16 @echo " make fmt - Apply gofmt/goimports"
17 @echo " make lint-auto - Run autofix lint/format pipeline"
18 - @echo " make test - Run Go tests, including the local multi-hop harness"
19 - @echo " make test-local-multihop - Run the focused local multi-hop relay harness"
18 + @echo " make test - Run Go tests"
19 @echo " make build - Build everything (frontend, tunnel, server)"
20 @echo " make build-frontend - Build React frontend (Tailwind CSS 4)"
21 @echo " make build-docs - Build documentation site (SvelteKit)"
@@ -48,9 +47,6 @@ lint-auto:
47 test:
48 go test -v -coverprofile=coverage.out $(GO_PACKAGES)
49
51 -test-local-multihop:
52 - go test -v ./portal -run 'TestLocalCluster'
53 -
50 vuln:
51 govulncheck $(GO_PACKAGES)
52
docs/src/routes/architecture/+page.md
-8
@@ -300,14 +300,6 @@ Result: raw public UDP exposure with an internal QUIC datagram backhaul. UDP and
300 - The overlay peer API is plain HTTP on the WireGuard network, not public Internet HTTP. It serves the same discovery payload shape used by public `/discovery`.
301 - Overlay failure affects inter-relay discovery, mesh synchronization, and multi-hop relay forwarding. Direct tenant TLS routing, keyless TLS, register/renew/connect, and public UDP ingress do not depend on the WireGuard transport path.
302
303 -### Local multi-hop test harness
304 -
305 -`portal/multihop_local_test.go` verifies the multi-hop relay path inside one `go test` process. It starts local relay servers on `127.0.0.1:0`, seeds signed local discovery descriptors, and uses test fakes only for the WireGuard transport layer.
306 -
307 -Use `make test-local-multihop` for the focused local harness. The same tests are also included in `make test` through the `./portal/...` package set. Use `go test ./portal ./sdk` when checking the narrower portal and SDK interaction without the full repository test suite.
308 -
309 -The harness exercises SDK expose/register logic, `/sdk/hop`, `RelaySet`, signed descriptors, route registration, SNI ingress, hop token matching, and `bridgeLeaseConn`. It intentionally excludes public DNS, ACME provider side effects, public registry bootstrap, and real WireGuard devices. `localRelaySpec` provides relay-level server and descriptor mutation hooks for future policy tests, and the fake overlay records synced peers so fake hop streams only open after the `/sdk/hop` overlay sync path has run.
310 -
303 ## Control Plane Flow
304
305 ### 1. Register
portal/api_server.go
+3 -3
@@ -334,7 +334,7 @@ func (s *Server) handleRegisterChallenge(w http.ResponseWriter, r *http.Request)
334 Path: types.PathSDKRegister,
335 }).String()
336
337 - if strings.TrimSpace(req.HopToken) != "" && !s.hasHopTransport() {
337 + if strings.TrimSpace(req.HopToken) != "" && s.overlay == nil {
338 utils.WriteAPIError(w, http.StatusServiceUnavailable, types.APIErrorCodeFeatureUnavailable, errFeatureUnavailable.Error())
339 return
340 }
@@ -408,7 +408,7 @@ func (s *Server) handleHop(w http.ResponseWriter, r *http.Request) {
408 utils.MethodNotAllowedError().Write(w)
409 return
410 }
411 - if !s.hasHopTransport() || !s.hasOverlayRuntime() || s.relaySet == nil {
411 + if s.overlay == nil || s.relaySet == nil {
412 utils.WriteAPIError(w, http.StatusServiceUnavailable, types.APIErrorCodeFeatureUnavailable, errFeatureUnavailable.Error())
413 return
414 }
@@ -461,7 +461,7 @@ func (s *Server) handleHop(w http.ResponseWriter, r *http.Request) {
461 utils.InvalidRequestError(fmt.Errorf("forward relay: %w", err)).Write(w)
462 return
463 }
464 - if err := s.syncOverlayPeers(s.relaySet.OverlayPeerDescriptor()); err != nil {
464 + if err := s.overlay.Sync(s.relaySet.OverlayPeerDescriptor()); err != nil {
465 utils.WriteAPIError(w, http.StatusInternalServerError, types.APIErrorCodeInternal, err.Error())
466 return
467 }
portal/multihop_local_test.go deleted
-459
@@ -1,459 +0,0 @@
1 -package portal
2 -
3 -import (
4 - "bufio"
5 - "context"
6 - "crypto/tls"
7 - "errors"
8 - "fmt"
9 - "io"
10 - "net"
11 - "net/http"
12 - "slices"
13 - "sync"
14 - "testing"
15 - "time"
16 -
17 - "github.com/gosuda/portal-tunnel/v2/portal/acme"
18 - "github.com/gosuda/portal-tunnel/v2/portal/auth"
19 - "github.com/gosuda/portal-tunnel/v2/portal/discovery"
20 - "github.com/gosuda/portal-tunnel/v2/portal/identity"
21 - "github.com/gosuda/portal-tunnel/v2/portal/overlay"
22 - "github.com/gosuda/portal-tunnel/v2/sdk"
23 - "github.com/gosuda/portal-tunnel/v2/types"
24 -)
25 -
26 -type localRelayCluster struct {
27 - relays []*localRelay
28 - byIP map[string]*localRelay
29 -}
30 -
31 -type localRelaySpec struct {
32 - Name string
33 - ServerMutator func(*Server)
34 - DescriptorMutator func(*types.RelayDescriptor)
35 -}
36 -
37 -type localRelay struct {
38 - name string
39 - server *Server
40 - apiURL string
41 - sniAddr string
42 - overlayIP string
43 - overlay *fakeOverlay
44 - descriptorMutator func(*types.RelayDescriptor)
45 -}
46 -
47 -type fakeOverlay struct {
48 - cfg overlay.Config
49 - mu sync.RWMutex
50 - syncedIPs map[string]struct{}
51 -}
52 -
53 -func (o *fakeOverlay) Config() overlay.Config {
54 - return o.cfg.Copy()
55 -}
56 -
57 -func (o *fakeOverlay) Sync(descriptors []types.RelayDescriptor) error {
58 - synced := make(map[string]struct{}, len(descriptors))
59 - for _, desc := range descriptors {
60 - overlayIP, err := identity.DeriveWireGuardOverlayIPv4(desc.WireGuardPublicKey)
61 - if err != nil {
62 - return err
63 - }
64 - synced[overlayIP] = struct{}{}
65 - }
66 - o.mu.Lock()
67 - o.syncedIPs = synced
68 - o.mu.Unlock()
69 - return nil
70 -}
71 -
72 -func (o *fakeOverlay) hasSyncedPeer(overlayIPv4 string) bool {
73 - o.mu.RLock()
74 - defer o.mu.RUnlock()
75 - _, ok := o.syncedIPs[overlayIPv4]
76 - return ok
77 -}
78 -
79 -type fakeHopMux struct {
80 - cluster *localRelayCluster
81 - overlay *fakeOverlay
82 -}
83 -
84 -func (m *fakeHopMux) OpenStream(ctx context.Context, overlayIPv4, token string) (net.Conn, error) {
85 - if m == nil || m.cluster == nil {
86 - return nil, errors.New("fake hop mux is not connected to a cluster")
87 - }
88 - if m.overlay == nil || !m.overlay.hasSyncedPeer(overlayIPv4) {
89 - return nil, fmt.Errorf("fake hop target %q was not synced to overlay", overlayIPv4)
90 - }
91 - target := m.cluster.byIP[overlayIPv4]
92 - if target == nil {
93 - return nil, fmt.Errorf("fake hop target %q not found", overlayIPv4)
94 - }
95 - left, right := net.Pipe()
96 - go target.bridgeFakeHop(ctx, right, token)
97 - return left, nil
98 -}
99 -
100 -func (r *localRelay) bridgeFakeHop(ctx context.Context, conn net.Conn, token string) {
101 - r.server.registry.mu.RLock()
102 - record := r.server.registry.recordByHopToken(token, time.Now())
103 - r.server.registry.mu.RUnlock()
104 - if record == nil {
105 - _ = conn.Close()
106 - return
107 - }
108 - if err := r.server.bridgeLeaseConn(ctx, conn, record); err != nil {
109 - _ = conn.Close()
110 - }
111 -}
112 -
113 -func newLocalRelayCluster(t *testing.T, names ...string) *localRelayCluster {
114 - t.Helper()
115 - specs := make([]localRelaySpec, 0, len(names))
116 - for _, name := range names {
117 - specs = append(specs, localRelaySpec{Name: name})
118 - }
119 - return newLocalRelayClusterFromSpecs(t, specs...)
120 -}
121 -
122 -func newLocalRelayClusterFromSpecs(t *testing.T, specs ...localRelaySpec) *localRelayCluster {
123 - t.Helper()
124 - if len(specs) == 0 {
125 - t.Fatal("local relay cluster requires at least one relay")
126 - }
127 -
128 - ctx, cancel := context.WithCancel(context.Background())
129 - cluster := &localRelayCluster{
130 - relays: make([]*localRelay, 0, len(specs)),
131 - byIP: make(map[string]*localRelay, len(specs)),
132 - }
133 - t.Cleanup(func() {
134 - cancel()
135 - for _, relay := range cluster.relays {
136 - _ = relay.server.Shutdown(context.Background())
137 - if err := relay.server.Wait(); err != nil {
138 - t.Fatalf("relay %s Wait() error = %v", relay.name, err)
139 - }
140 - }
141 - })
142 -
143 - for _, spec := range specs {
144 - relay := startLocalRelay(t, ctx, spec)
145 - cluster.relays = append(cluster.relays, relay)
146 - }
147 - cluster.seedDiscovery(t)
148 - return cluster
149 -}
150 -
151 -func startLocalRelay(t *testing.T, ctx context.Context, spec localRelaySpec) *localRelay {
152 - t.Helper()
153 - name := spec.Name
154 - if name == "" {
155 - t.Fatal("local relay spec name is required")
156 - }
157 - server, err := NewServer(ServerConfig{
158 - PortalURL: "https://localhost:4017",
159 - IdentityPath: tempIdentityPath(t),
160 - ACME: acme.Config{KeyDir: t.TempDir()},
161 - APIListenAddr: "127.0.0.1:0",
162 - SNIListenAddr: "127.0.0.1:0",
163 - })
164 - if err != nil {
165 - t.Fatalf("NewServer(%s) error = %v", name, err)
166 - }
167 - if err := server.Start(ctx, nil); err != nil {
168 - t.Fatalf("Start(%s) error = %v", name, err)
169 - }
170 -
171 - apiPort := mustPort(t, server.apiListener.Addr().String())
172 - sniPort := mustPort(t, server.sniListener.Addr().String())
173 - apiURL := "https://localhost:" + apiPort
174 - server.cfg.PortalURL = apiURL
175 - server.cfg.SNIPort = mustAtoi(t, sniPort)
176 - server.registry.sniPort = server.cfg.SNIPort
177 - if spec.ServerMutator != nil {
178 - spec.ServerMutator(server)
179 - }
180 -
181 - wgPrivate, err := identity.GenerateWireGuardPrivateKey()
182 - if err != nil {
183 - t.Fatalf("identity.GenerateWireGuardPrivateKey(%s) error = %v", name, err)
184 - }
185 - wgPublic, err := identity.WireGuardPublicKeyFromPrivate(wgPrivate)
186 - if err != nil {
187 - t.Fatalf("identity.WireGuardPublicKeyFromPrivate(%s) error = %v", name, err)
188 - }
189 - fakeOverlay := &fakeOverlay{cfg: overlay.Config{
190 - PublicKey: wgPublic,
191 - ListenPort: overlay.DefaultListenPort,
192 - }}
193 - fakeHopMux := &fakeHopMux{overlay: fakeOverlay}
194 - server.testHooks = &serverTestHooks{
195 - overlayConfig: fakeOverlay.Config,
196 - syncOverlayPeers: fakeOverlay.Sync,
197 - openHopStream: fakeHopMux.OpenStream,
198 - }
199 - server.cfg.DiscoveryEnabled = true
200 -
201 - overlayIP, err := identity.DeriveWireGuardOverlayIPv4(wgPublic)
202 - if err != nil {
203 - t.Fatalf("identity.DeriveWireGuardOverlayIPv4(%s) error = %v", name, err)
204 - }
205 - return &localRelay{
206 - name: name,
207 - server: server,
208 - apiURL: apiURL,
209 - sniAddr: net.JoinHostPort("127.0.0.1", sniPort),
210 - overlayIP: overlayIP,
211 - overlay: fakeOverlay,
212 - descriptorMutator: spec.DescriptorMutator,
213 - }
214 -}
215 -
216 -func (c *localRelayCluster) seedDiscovery(t *testing.T) {
217 - t.Helper()
218 - urls := make([]string, 0, len(c.relays))
219 - descriptors := make([]types.RelayDescriptor, 0, len(c.relays))
220 - for _, relay := range c.relays {
221 - urls = append(urls, relay.apiURL)
222 - desc, err := relay.server.newSelfDescriptor(time.Now())
223 - if err != nil {
224 - t.Fatalf("newSelfDescriptor(%s) error = %v", relay.name, err)
225 - }
226 - if relay.descriptorMutator != nil {
227 - relay.descriptorMutator(&desc)
228 - desc, err = auth.SignRelayDescriptor(desc, relay.server.authority)
229 - if err != nil {
230 - t.Fatalf("SignRelayDescriptor(%s) error = %v", relay.name, err)
231 - }
232 - }
233 - descriptors = append(descriptors, desc)
234 - c.byIP[relay.overlayIP] = relay
235 - }
236 - for _, relay := range c.relays {
237 - relay.server.relaySet = discovery.NewRelaySet(urls)
238 - fakeHopMux := &fakeHopMux{cluster: c, overlay: relay.overlay}
239 - relay.server.testHooks.openHopStream = fakeHopMux.OpenStream
240 - changed, err := relay.server.relaySet.ApplyRelayDiscoveryResponse(relay.apiURL, types.DiscoveryResponse{
241 - ProtocolVersion: types.DiscoveryVersion,
242 - GeneratedAt: time.Now().UTC(),
243 - Relays: descriptors,
244 - }, time.Now())
245 - if err != nil {
246 - t.Fatalf("ApplyRelayDiscoveryResponse(%s) error = %v", relay.name, err)
247 - }
248 - if !changed {
249 - t.Fatalf("ApplyRelayDiscoveryResponse(%s) changed = false, want true", relay.name)
250 - }
251 - }
252 -}
253 -
254 -func (c *localRelayCluster) relay(index int) *localRelay {
255 - return c.relays[index]
256 -}
257 -
258 -func (c *localRelayCluster) relayURLs() []string {
259 - out := make([]string, 0, len(c.relays))
260 - for _, relay := range c.relays {
261 - out = append(out, relay.apiURL)
262 - }
263 - return out
264 -}
265 -
266 -func (c *localRelayCluster) exposeMultiHop(t *testing.T, ctx context.Context, name string) *sdk.Exposure {
267 - t.Helper()
268 - exposure, err := sdk.Expose(ctx, sdk.ExposeConfig{
269 - Identity: types.Identity{Name: name},
270 - TargetAddr: "127.0.0.1:1",
271 - MultiHop: c.relayURLs(),
272 - BanMITM: false,
273 - })
274 - if err != nil {
275 - t.Fatalf("sdk.Expose() error = %v", err)
276 - }
277 - t.Cleanup(func() {
278 - _ = exposure.Close()
279 - })
280 - return exposure
281 -}
282 -
283 -func TestLocalClusterExplicitMultiHopRegistersRoutes(t *testing.T) {
284 - cluster := newLocalRelayCluster(t, "entry", "middle", "exit")
285 - ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
286 - defer cancel()
287 -
288 - exposure := cluster.exposeMultiHop(t, ctx, "local-hop")
289 - waitForLocalHopRoutes(t, cluster, "local-hop.localhost")
290 -
291 - if got := exposure.ActiveRelayURLs(); len(got) != 1 || got[0] != cluster.relay(2).apiURL {
292 - t.Fatalf("ActiveRelayURLs() = %v, want exit relay %q", got, cluster.relay(2).apiURL)
293 - }
294 - entryRecord, ok := cluster.relay(0).server.registry.Lookup("local-hop.localhost")
295 - if !ok {
296 - t.Fatal("entry relay public hostname lookup failed")
297 - }
298 - if _, _, hasNext := entryRecord.nextHop(); !hasNext {
299 - t.Fatal("entry relay route has no next hop")
300 - }
301 - if middle := firstRecord(cluster.relay(1).server, func(record *leaseRecord) bool {
302 - return record.isHopMiddle()
303 - }); middle == nil {
304 - t.Fatal("middle relay hop route missing")
305 - }
306 - if exit := firstRecord(cluster.relay(2).server, func(record *leaseRecord) bool {
307 - return record.isHopExit() && record.stream != nil
308 - }); exit == nil {
309 - t.Fatal("exit relay stream lease missing")
310 - }
311 -}
312 -
313 -func TestLocalClusterPublicIngressTraversesFakeHopChain(t *testing.T) {
314 - cluster := newLocalRelayCluster(t, "entry", "middle", "exit")
315 - ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
316 - defer cancel()
317 -
318 - exposure := cluster.exposeMultiHop(t, ctx, "local-http")
319 - handlerReady := make(chan error, 1)
320 - go func() {
321 - handlerReady <- exposure.RunHTTP(ctx, http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
322 - _, _ = io.WriteString(w, "local multi-hop ok")
323 - }), "")
324 - }()
325 - waitForLocalHopRoutes(t, cluster, "local-http.localhost")
326 -
327 - body := requestThroughEntry(t, cluster.relay(0), "local-http.localhost")
328 - if body != "local multi-hop ok" {
329 - t.Fatalf("public ingress body = %q, want %q", body, "local multi-hop ok")
330 - }
331 -
332 - cancel()
333 - if err := <-handlerReady; err != nil && !errors.Is(err, context.Canceled) {
334 - t.Fatalf("RunHTTP() error = %v", err)
335 - }
336 -}
337 -
338 -func TestLocalClusterDiscoveryStaysLocal(t *testing.T) {
339 - cluster := newLocalRelayCluster(t, "entry", "middle", "exit")
340 - want := cluster.relayURLs()
341 - slices.Sort(want)
342 -
343 - for _, relay := range cluster.relays {
344 - self, err := relay.server.newSelfDescriptor(time.Now())
345 - if err != nil {
346 - t.Fatalf("newSelfDescriptor(%s) error = %v", relay.name, err)
347 - }
348 - descriptors := relay.server.relaySet.Descriptors(self)
349 - got := make([]string, 0, len(descriptors))
350 - for _, desc := range descriptors {
351 - got = append(got, desc.APIHTTPSAddr)
352 - }
353 - slices.Sort(got)
354 - if !slices.Equal(got, want) {
355 - t.Fatalf("discovery relays for %s = %v, want local relays %v", relay.name, got, want)
356 - }
357 - }
358 -}
359 -
360 -func waitForLocalHopRoutes(t *testing.T, cluster *localRelayCluster, publicHostname string) {
361 - t.Helper()
362 - eventually(t, 10*time.Second, func() (bool, string) {
363 - if _, ok := cluster.relay(0).server.registry.Lookup(publicHostname); !ok {
364 - return false, "entry public route missing"
365 - }
366 - if firstRecord(cluster.relay(1).server, func(record *leaseRecord) bool { return record.isHopMiddle() }) == nil {
367 - return false, "middle hop route missing"
368 - }
369 - if firstRecord(cluster.relay(2).server, func(record *leaseRecord) bool {
370 - return record.isHopExit() && record.stream != nil && record.stream.ReadyCount() > 0
371 - }) == nil {
372 - return false, "exit stream lease not ready"
373 - }
374 - return true, ""
375 - })
376 -}
377 -
378 -func firstRecord(server *Server, match func(*leaseRecord) bool) *leaseRecord {
379 - server.registry.mu.RLock()
380 - defer server.registry.mu.RUnlock()
381 - for _, record := range server.registry.records {
382 - if record != nil && match(record) {
383 - return record
384 - }
385 - }
386 - return nil
387 -}
388 -
389 -func requestThroughEntry(t *testing.T, entry *localRelay, serverName string) string {
390 - t.Helper()
391 - conn, err := tls.DialWithDialer(&net.Dialer{Timeout: 5 * time.Second}, "tcp", entry.sniAddr, &tls.Config{
392 - MinVersion: tls.VersionTLS12,
393 - ServerName: serverName,
394 - InsecureSkipVerify: true,
395 - NextProtos: []string{"http/1.1"},
396 - })
397 - if err != nil {
398 - t.Fatalf("dial entry SNI listener: %v", err)
399 - }
400 - defer conn.Close()
401 -
402 - req, err := http.NewRequest(http.MethodGet, "https://"+serverName+"/", nil)
403 - if err != nil {
404 - t.Fatalf("NewRequest() error = %v", err)
405 - }
406 - if err := req.Write(conn); err != nil {
407 - t.Fatalf("write HTTP request: %v", err)
408 - }
409 - resp, err := http.ReadResponse(bufioNewReader(conn), req)
410 - if err != nil {
411 - t.Fatalf("read HTTP response: %v", err)
412 - }
413 - defer resp.Body.Close()
414 - body, err := io.ReadAll(resp.Body)
415 - if err != nil {
416 - t.Fatalf("read HTTP response body: %v", err)
417 - }
418 - if resp.StatusCode != http.StatusOK {
419 - t.Fatalf("GET through entry status = %d body=%q, want 200", resp.StatusCode, string(body))
420 - }
421 - return string(body)
422 -}
423 -
424 -func eventually(t *testing.T, timeout time.Duration, check func() (bool, string)) {
425 - t.Helper()
426 - deadline := time.Now().Add(timeout)
427 - var last string
428 - for time.Now().Before(deadline) {
429 - ok, reason := check()
430 - if ok {
431 - return
432 - }
433 - last = reason
434 - time.Sleep(25 * time.Millisecond)
435 - }
436 - t.Fatalf("condition not met within %s: %s", timeout, last)
437 -}
438 -
439 -func mustPort(t *testing.T, addr string) string {
440 - t.Helper()
441 - _, port, err := net.SplitHostPort(addr)
442 - if err != nil {
443 - t.Fatalf("SplitHostPort(%q) error = %v", addr, err)
444 - }
445 - return port
446 -}
447 -
448 -func mustAtoi(t *testing.T, raw string) int {
449 - t.Helper()
450 - var out int
451 - if _, err := fmt.Sscanf(raw, "%d", &out); err != nil {
452 - t.Fatalf("parse int %q: %v", raw, err)
453 - }
454 - return out
455 -}
456 -
457 -func bufioNewReader(conn net.Conn) *bufio.Reader {
458 - return bufio.NewReader(conn)
459 -}
portal/server.go
+5 -56
@@ -131,13 +131,6 @@ type Server struct {
131 relaySet *discovery.RelaySet
132 announceLimiter *discovery.AnnounceLimiter
133 registry *leaseRegistry
134 - testHooks *serverTestHooks
135 -}
136 -
137 -type serverTestHooks struct {
138 - overlayConfig func() overlay.Config
139 - syncOverlayPeers func([]types.RelayDescriptor) error
140 - openHopStream func(context.Context, string, string) (net.Conn, error)
134 }
135
136 func NewServer(cfg ServerConfig) (*Server, error) {
@@ -560,6 +553,8 @@ func (s *Server) bridgeLeaseConn(ctx context.Context, conn net.Conn, record *lea
553 }
554 if overlayIPv4, forwardToken, hasNextHop := record.nextHop(); hasNextHop {
555 switch {
556 + case s.overlay == nil:
557 + return errors.New("relay overlay is unavailable")
558 case overlayIPv4 == "":
559 return errors.New("next hop overlay ipv4 is required")
560 case forwardToken == "":
@@ -572,7 +567,7 @@ func (s *Server) bridgeLeaseConn(ctx context.Context, conn net.Conn, record *lea
567 var lastErr error
568 for {
569 var err error
575 - next, err = s.openHopStream(openCtx, overlayIPv4, forwardToken)
570 + next, err = s.overlay.OpenHopStream(openCtx, overlayIPv4, forwardToken)
571 if err == nil {
572 break
573 }
@@ -603,53 +598,6 @@ func (s *Server) bridgeLeaseConn(ctx context.Context, conn net.Conn, record *lea
598 return nil
599 }
600
606 -func (s *Server) hasHopTransport() bool {
607 - return s != nil && (s.overlay != nil || (s.testHooks != nil && s.testHooks.openHopStream != nil))
608 -}
609 -
610 -func (s *Server) hasOverlayRuntime() bool {
611 - return s != nil && (s.overlay != nil || (s.testHooks != nil && s.testHooks.overlayConfig != nil && s.testHooks.syncOverlayPeers != nil))
612 -}
613 -
614 -func (s *Server) currentOverlayConfig() (overlay.Config, bool) {
615 - if s == nil {
616 - return overlay.Config{}, false
617 - }
618 - if s.overlay != nil {
619 - return s.overlay.Config(), true
620 - }
621 - if s.testHooks != nil && s.testHooks.overlayConfig != nil {
622 - return s.testHooks.overlayConfig(), true
623 - }
624 - return overlay.Config{}, false
625 -}
626 -
627 -func (s *Server) syncOverlayPeers(descriptors []types.RelayDescriptor) error {
628 - if s == nil {
629 - return errors.New("server is unavailable")
630 - }
631 - if s.overlay != nil {
632 - return s.overlay.Sync(descriptors)
633 - }
634 - if s.testHooks != nil && s.testHooks.syncOverlayPeers != nil {
635 - return s.testHooks.syncOverlayPeers(descriptors)
636 - }
637 - return errors.New("relay overlay is unavailable")
638 -}
639 -
640 -func (s *Server) openHopStream(ctx context.Context, overlayIPv4, token string) (net.Conn, error) {
641 - if s == nil {
642 - return nil, errors.New("server is unavailable")
643 - }
644 - if s.overlay != nil {
645 - return s.overlay.OpenHopStream(ctx, overlayIPv4, token)
646 - }
647 - if s.testHooks != nil && s.testHooks.openHopStream != nil {
648 - return s.testHooks.openHopStream(ctx, overlayIPv4, token)
649 - }
650 - return nil, errors.New("relay overlay is unavailable")
651 -}
652 -
601 func (s *Server) runRegistryJanitor(ctx context.Context, interval time.Duration) error {
602 if interval <= 0 {
603 return errors.New("janitor interval must be positive")
@@ -826,7 +774,8 @@ func (s *Server) newSelfDescriptor(now time.Time) (types.RelayDescriptor, error)
774 var wireGuardPublicKey string
775 var wireGuardPort int
776 supportsOverlay := false
829 - if cfg, ok := s.currentOverlayConfig(); ok {
777 + if s.overlay != nil {
778 + cfg := s.overlay.Config()
779 wireGuardPublicKey = cfg.PublicKey
780 wireGuardPort = cfg.ListenPort
781 supportsOverlay = true