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