refact: tidy codes

Kim committed Mar 31, 2026 at 13:55 UTC 9aef374cbe8e70a50c1a9db884a0c0c017ba1dea
10 files changed +157 -177
cmd/relay-server/main.go
-15
@@ -155,21 +155,6 @@ func runServer(ctx context.Context, cfg relayServerConfig) error {
155 return fmt.Errorf("start relay server: %w", err)
156 }
157
158 - rootHost := server.RootHost()
159 - logEvent := log.Info().
160 - Str("api_addr", utils.HostPortOrLoopback(server.APIAddr())).
161 - Str("sni_addr", server.SNIAddr()).
162 - Str("root_host", rootHost).
163 - Str("acme_dns_provider", cfg.ACMEDNSProvider).
164 - Bool("discovery_enabled", server.DiscoveryEnabled()).
165 - Bool("wireguard_enabled", strings.TrimSpace(cfg.WireGuardPrivateKey) != "").
166 - Bool("udp_enabled", cfg.UDPPortCount > 0).
167 - Bool("acme_enabled", !strings.HasSuffix(rootHost, "localhost") && rootHost != "127.0.0.1" && rootHost != "::1")
168 - if quicAddr := server.QUICTunnelAddr(); quicAddr != "" {
169 - logEvent = logEvent.Str("internal_quic_tunnel_addr", quicAddr)
170 - }
171 - logEvent.Msg("relay server started")
172 -
158 return server.Wait()
159 }
160
portal/api_server.go
+5 -5
@@ -84,7 +84,7 @@ func (s *Server) apiHandler(base *http.ServeMux, keylessSignerHandler http.Handl
84 case types.PathSDKConnect:
85 s.handleConnect(w, r)
86 case types.PathDiscovery:
87 - if !s.DiscoveryEnabled() {
87 + if !s.cfg.DiscoveryEnabled {
88 base.ServeHTTP(w, r)
89 return
90 }
@@ -133,7 +133,7 @@ func (s *Server) handleRelayDiscovery(w http.ResponseWriter, r *http.Request) {
133 Sequence: uint64(now.UnixMilli()),
134 Version: 1,
135 IssuedAt: now,
136 - ExpiresAt: now.Add(2 * defaultDiscoveryInterval),
136 + ExpiresAt: now.Add(2 * types.DiscoveryPollInterval),
137 APIHTTPSAddr: s.cfg.PortalURL,
138 IngressTLSAddr: ingressAddr,
139 SupportsTCP: true,
@@ -307,7 +307,7 @@ func (s *Server) handleRenew(w http.ResponseWriter, r *http.Request) {
307 return
308 }
309
310 - ttl := s.cfg.LeaseTTL
310 + ttl := defaultLeaseTTL
311 if req.TTL > 0 {
312 ttl = time.Duration(req.TTL) * time.Second
313 }
@@ -540,7 +540,7 @@ func (s *Server) registerLease(req types.RegisterChallengeRequest, clientIP, rep
540 return types.RegisterResponse{}, err
541 }
542
543 - ttl := s.cfg.LeaseTTL
543 + ttl := defaultLeaseTTL
544 if req.TTL > 0 {
545 ttl = time.Duration(req.TTL) * time.Second
546 }
@@ -584,7 +584,7 @@ func (s *Server) registerLease(req types.RegisterChallengeRequest, clientIP, rep
584 ReportedIP: utils.SanitizeReportedIP(reportedIP),
585 UDPEnabled: req.UDPEnabled,
586 },
587 - stream: transport.NewRelayStream(leaseID, s.cfg.IdleKeepaliveInterval, s.cfg.ReadyQueueLimit),
587 + stream: transport.NewRelayStream(leaseID, defaultIdleKeepalive, defaultReadyQueueLimit),
588 }
589 if req.UDPEnabled {
590 if s.ports == nil {
portal/discovery/discovery.go
+33 -52
@@ -2,7 +2,6 @@ package discovery
2
3 import (
4 "context"
5 - "crypto/tls"
5 "errors"
6 "fmt"
7 "net/http"
@@ -153,13 +152,45 @@ func ValidateDescriptorTarget(desc types.RelayDescriptor, targetRelayID, targetU
152 }
153
154 func DiscoverRelayDiscovery(ctx context.Context, baseURL string, rootCAPEM []byte, httpClient *http.Client) (types.DiscoveryResponse, error) {
156 - resp, err := doGET[types.DiscoveryResponse](ctx, baseURL, types.PathDiscovery, nil, rootCAPEM, httpClient)
155 + parsedBaseURL, err := url.Parse(baseURL)
156 if err != nil {
157 + return types.DiscoveryResponse{}, fmt.Errorf("parse discovery base url: %w", err)
158 + }
159 +
160 + requestURL := parsedBaseURL.ResolveReference(&url.URL{Path: types.PathDiscovery})
161 +
162 + client := httpClient
163 + if client == nil {
164 + _, client, err = keyless.NewRelayHTTPClient(ctx, parsedBaseURL, rootCAPEM, defaultRequestTimeout)
165 + if err != nil {
166 + return types.DiscoveryResponse{}, err
167 + }
168 + }
169 + if client.Timeout == 0 {
170 + clone := *client
171 + clone.Timeout = defaultRequestTimeout
172 + client = &clone
173 + }
174 +
175 + var resp types.DiscoveryResponse
176 + if err := utils.HTTPDoAPI(ctx, client, http.MethodGet, requestURL.String(), nil, nil, &resp); err != nil {
177 return types.DiscoveryResponse{}, err
178 }
179 return resp, nil
180 }
181
182 +func DiscoveryUnavailableStatus(err error) (statusCode int, code string, unavailable bool) {
183 + var apiErr *types.APIRequestError
184 + if !errors.As(err, &apiErr) || apiErr == nil {
185 + return 0, "", false
186 + }
187 + code = strings.TrimSpace(apiErr.Code)
188 + if apiErr.StatusCode == http.StatusNotFound || code == types.APIErrorCodeFeatureUnavailable {
189 + return apiErr.StatusCode, code, true
190 + }
191 + return 0, "", false
192 +}
193 +
194 func SeedDescriptor(apiURL string) (types.RelayDescriptor, error) {
195 normalized, err := utils.NormalizeRelayURL(apiURL)
196 if err != nil {
@@ -187,53 +218,3 @@ func RequireOverlayRelayDescriptor(desc types.RelayDescriptor) error {
218 }
219 return nil
220 }
190 -
191 -func doGET[T any](ctx context.Context, baseURL, path string, query url.Values, rootCAPEM []byte, httpClient *http.Client) (T, error) {
192 - var zero T
193 -
194 - baseURL = strings.TrimSpace(baseURL)
195 - if baseURL == "" {
196 - return zero, errors.New("discovery base url is required")
197 - }
198 - parsedBaseURL, err := url.Parse(baseURL)
199 - if err != nil {
200 - return zero, fmt.Errorf("parse discovery base url: %w", err)
201 - }
202 - if parsedBaseURL.Host == "" {
203 - return zero, errors.New("discovery base url host is required")
204 - }
205 -
206 - requestURL := parsedBaseURL.ResolveReference(&url.URL{Path: path})
207 - if query != nil {
208 - requestURL.RawQuery = query.Encode()
209 - }
210 -
211 - client := httpClient
212 - if client == nil {
213 - rootCAs, err := keyless.RelayRootCAs(ctx, baseURL, parsedBaseURL.Hostname(), rootCAPEM)
214 - if err != nil {
215 - return zero, err
216 - }
217 - client = &http.Client{
218 - Transport: &http.Transport{
219 - TLSClientConfig: &tls.Config{
220 - MinVersion: tls.VersionTLS12,
221 - ServerName: parsedBaseURL.Hostname(),
222 - RootCAs: rootCAs,
223 - NextProtos: []string{"http/1.1"},
224 - },
225 - ForceAttemptHTTP2: false,
226 - },
227 - }
228 - }
229 - if client.Timeout == 0 {
230 - clone := *client
231 - clone.Timeout = defaultRequestTimeout
232 - client = &clone
233 - }
234 -
235 - if err := utils.HTTPDoAPI(ctx, client, http.MethodGet, requestURL.String(), nil, nil, &zero); err != nil {
236 - return zero, err
237 - }
238 - return zero, nil
239 -}
portal/discovery/relayset.go
+23
@@ -350,6 +350,29 @@ func (s *RelaySet) MarkRelayFailure(relayURL string, now time.Time) RelayLocalSt
350 return state
351 }
352
353 +func (s *RelaySet) RecordBootstrapDiscoveryFailure(relayURL string, err error, now time.Time) {
354 + state := s.MarkRelayFailure(relayURL, now)
355 + if statusCode, code, unavailable := DiscoveryUnavailableStatus(err); unavailable {
356 + if state.ConsecutiveFailures > 1 {
357 + return
358 + }
359 + event := log.Info().Str("relay", relayURL)
360 + if statusCode > 0 {
361 + event = event.Int("status_code", statusCode)
362 + }
363 + if code != "" {
364 + event = event.Str("code", code)
365 + }
366 + event.Msg("bootstrap relay discovery unavailable; peer may have discovery disabled")
367 + return
368 + }
369 +
370 + log.Warn().
371 + Err(err).
372 + Str("relay", relayURL).
373 + Msg("bootstrap relay discovery failed")
374 +}
375 +
376 func (s *RelaySet) AdvertisedDescriptors() []types.RelayDescriptor {
377 if s == nil {
378 return nil
portal/keyless/http.go new
+42
@@ -0,0 +1,42 @@
1 +package keyless
2 +
3 +import (
4 + "context"
5 + "crypto/tls"
6 + "errors"
7 + "net/http"
8 + "net/url"
9 + "strings"
10 + "time"
11 +)
12 +
13 +func NewRelayHTTPClient(ctx context.Context, relayURL *url.URL, rootCAPEM []byte, timeout time.Duration) (*tls.Config, *http.Client, error) {
14 + if relayURL == nil {
15 + return nil, nil, errors.New("relay url is required")
16 + }
17 +
18 + serverName := strings.TrimSpace(relayURL.Hostname())
19 + if serverName == "" {
20 + return nil, nil, errors.New("relay hostname is required")
21 + }
22 +
23 + rootCAs, err := RelayRootCAs(ctx, relayURL.String(), serverName, rootCAPEM)
24 + if err != nil {
25 + return nil, nil, err
26 + }
27 +
28 + rawTLSConfig := &tls.Config{
29 + MinVersion: tls.VersionTLS12,
30 + ServerName: serverName,
31 + RootCAs: rootCAs,
32 + NextProtos: []string{"http/1.1"},
33 + }
34 + httpClient := &http.Client{
35 + Transport: &http.Transport{
36 + TLSClientConfig: rawTLSConfig.Clone(),
37 + ForceAttemptHTTP2: false,
38 + },
39 + Timeout: timeout,
40 + }
41 + return rawTLSConfig, httpClient, nil
42 +}
portal/server.go
+39 -79
@@ -30,7 +30,6 @@ import (
30 const (
31 defaultLeaseTTL = 30 * time.Second
32 defaultClaimTimeout = 10 * time.Second
33 - defaultDiscoveryInterval = 30 * time.Second
33 defaultIdleKeepalive = 15 * time.Second
34 defaultReadyQueueLimit = 8
35 defaultClientHelloWait = 2 * time.Second
@@ -40,30 +39,25 @@ const (
39 )
40
41 type ServerConfig struct {
43 - PortalURL string
44 - OwnerPrivateKey string
45 - Bootstraps []string
46 - WireGuardPrivateKey string
47 - DiscoveryPort int
48 - WireGuardPublicKey string
49 - WireGuardEndpoint string
50 - OverlayIPv4 string
51 - OverlayCIDRs []string
52 - ACME acme.Config
53 - APIPort int
54 - SNIPort int
55 - APIListenAddr string
56 - SNIListenAddr string
57 - QUICListenAddr string
58 - TrustedProxyCIDRs string
59 - LeaseTTL time.Duration
60 - ClaimTimeout time.Duration
61 - IdleKeepaliveInterval time.Duration
62 - ReadyQueueLimit int
63 - ClientHelloTimeout time.Duration
64 - TrustProxyHeaders bool
65 - DiscoveryEnabled bool
66 - UDPPortCount int
42 + PortalURL string
43 + OwnerPrivateKey string
44 + Bootstraps []string
45 + WireGuardPrivateKey string
46 + DiscoveryPort int
47 + WireGuardPublicKey string
48 + WireGuardEndpoint string
49 + OverlayIPv4 string
50 + OverlayCIDRs []string
51 + ACME acme.Config
52 + APIPort int
53 + SNIPort int
54 + APIListenAddr string
55 + SNIListenAddr string
56 + QUICListenAddr string
57 + TrustedProxyCIDRs string
58 + TrustProxyHeaders bool
59 + DiscoveryEnabled bool
60 + UDPPortCount int
61 }
62
63 type Server struct {
@@ -93,11 +87,6 @@ func NewServer(cfg ServerConfig) (*Server, error) {
87 cfg.SNIPort = utils.IntOrDefault(cfg.SNIPort, 443)
88 cfg.APIListenAddr = utils.StringOrDefault(cfg.APIListenAddr, fmt.Sprintf(":%d", cfg.APIPort))
89 cfg.SNIListenAddr = utils.StringOrDefault(cfg.SNIListenAddr, fmt.Sprintf(":%d", cfg.SNIPort))
96 - cfg.LeaseTTL = utils.DurationOrDefault(cfg.LeaseTTL, defaultLeaseTTL)
97 - cfg.ClaimTimeout = utils.DurationOrDefault(cfg.ClaimTimeout, defaultClaimTimeout)
98 - cfg.IdleKeepaliveInterval = utils.DurationOrDefault(cfg.IdleKeepaliveInterval, defaultIdleKeepalive)
99 - cfg.ReadyQueueLimit = utils.IntOrDefault(cfg.ReadyQueueLimit, defaultReadyQueueLimit)
100 - cfg.ClientHelloTimeout = utils.DurationOrDefault(cfg.ClientHelloTimeout, defaultClientHelloWait)
90 rootHost := utils.PortalRootHost(cfg.PortalURL)
91 if rootHost == "" {
92 return nil, errors.New("root host is required")
@@ -246,7 +235,7 @@ func (s *Server) Start(ctx context.Context, apiMux *http.ServeMux) error {
235 }
236 group.Go(func() error { return s.runSNIListener(groupCtx) })
237 group.Go(func() error { return s.registry.RunJanitor(groupCtx, 5*time.Second) })
249 - if s.DiscoveryEnabled() {
238 + if s.cfg.DiscoveryEnabled {
239 group.Go(func() error { return s.runRelayDiscoveryLoop(groupCtx) })
240 }
241 s.acmeManager.Start(serverCtx)
@@ -263,6 +252,20 @@ func (s *Server) Start(ctx context.Context, apiMux *http.ServeMux) error {
252 return s.Shutdown(shutdownCtx)
253 })
254
255 + logEvent := log.Info().
256 + Str("api_addr", utils.HostPortOrLoopback(s.apiListener.Addr().String())).
257 + Str("sni_addr", s.sniListener.Addr().String()).
258 + Str("root_host", s.rootHost).
259 + Str("acme_dns_provider", s.cfg.ACME.DNSProvider).
260 + Bool("discovery_enabled", s.cfg.DiscoveryEnabled).
261 + Bool("wireguard_enabled", strings.TrimSpace(s.wgConfig.PrivateKey) != "").
262 + Bool("udp_enabled", s.cfg.UDPPortCount > 0).
263 + Bool("acme_enabled", !strings.HasSuffix(s.rootHost, "localhost") && s.rootHost != "127.0.0.1" && s.rootHost != "::1")
264 + if s.quicTunnel != nil {
265 + logEvent = logEvent.Str("internal_quic_tunnel_addr", s.quicTunnel.Addr().String())
266 + }
267 + logEvent.Msg("relay server started")
268 +
269 return nil
270 }
271
@@ -321,31 +324,6 @@ func (s *Server) PolicyRuntime() *policy.Runtime {
324 return s.registry.policy
325 }
326
324 -func (s *Server) APIAddr() string {
325 - if s.apiListener == nil {
326 - return ""
327 - }
328 - return s.apiListener.Addr().String()
329 -}
330 -
331 -func (s *Server) SNIAddr() string {
332 - if s.sniListener == nil {
333 - return ""
334 - }
335 - return s.sniListener.Addr().String()
336 -}
337 -
338 -func (s *Server) QUICTunnelAddr() string {
339 - if s.quicTunnel == nil {
340 - return ""
341 - }
342 - return s.quicTunnel.Addr().String()
343 -}
344 -
345 -func (s *Server) DiscoveryEnabled() bool {
346 - return s != nil && s.cfg.DiscoveryEnabled
347 -}
348 -
327 func (s *Server) PortalURL() string {
328 if s == nil {
329 return ""
@@ -353,20 +331,6 @@ func (s *Server) PortalURL() string {
331 return s.cfg.PortalURL
332 }
333
356 -func (s *Server) OwnerAddress() string {
357 - if s == nil {
358 - return ""
359 - }
360 - return s.ownerIdentity.Address
361 -}
362 -
363 -func (s *Server) RootHost() string {
364 - if s == nil {
365 - return ""
366 - }
367 - return s.rootHost
368 -}
369 -
334 func (s *Server) LeaseSnapshots() []types.Lease {
335 s.registry.mu.RLock()
336 defer s.registry.mu.RUnlock()
@@ -434,7 +398,7 @@ func (s *Server) runSNIListener(ctx context.Context) error {
398 switch {
399 case err == nil:
400 go func(conn net.Conn) {
437 - clientHello, wrappedConn, err := l4.InspectClientHello(conn, s.cfg.ClientHelloTimeout)
401 + clientHello, wrappedConn, err := l4.InspectClientHello(conn, defaultClientHelloWait)
402 if err != nil {
403 if wrappedConn != nil {
404 _ = wrappedConn.Close()
@@ -471,7 +435,7 @@ func (s *Server) runSNIListener(ctx context.Context) error {
435 return
436 }
437
474 - claimCtx, cancel := context.WithTimeout(ctx, s.cfg.ClaimTimeout)
438 + claimCtx, cancel := context.WithTimeout(ctx, defaultClaimTimeout)
439 defer cancel()
440
441 session, err := record.stream.Claim(claimCtx)
@@ -543,7 +507,7 @@ func (s *Server) startOverlay() error {
507 peerMux.HandleFunc(types.PathRoot, s.handleRoot)
508 peerMux.HandleFunc(types.PathHealthz, s.handleHealthz)
509 peerMux.HandleFunc(types.PathDiscovery, func(w http.ResponseWriter, r *http.Request) {
546 - if !s.DiscoveryEnabled() {
510 + if !s.cfg.DiscoveryEnabled {
511 http.NotFound(w, r)
512 return
513 }
@@ -564,7 +528,7 @@ func (s *Server) startOverlay() error {
528 return nil
529 }
530 func (s *Server) runRelayDiscoveryLoop(ctx context.Context) error {
567 - ticker := time.NewTicker(defaultDiscoveryInterval)
531 + ticker := time.NewTicker(types.DiscoveryPollInterval)
532 defer ticker.Stop()
533
534 for {
@@ -576,11 +540,7 @@ func (s *Server) runRelayDiscoveryLoop(ctx context.Context) error {
540 if ctx.Err() != nil {
541 return nil
542 }
579 - s.relaySet.MarkRelayFailure(bootstrap.APIHTTPSAddr, time.Now().UTC())
580 - log.Warn().
581 - Err(err).
582 - Str("relay", bootstrap.APIHTTPSAddr).
583 - Msg("bootstrap relay discovery failed")
543 + s.relaySet.RecordBootstrapDiscoveryFailure(bootstrap.APIHTTPSAddr, err, time.Now().UTC())
544 continue
545 }
546
portal/server_test.go
+8 -8
@@ -112,7 +112,7 @@ func TestServerStartInitializesLocalACMEAndSigner(t *testing.T) {
112 }
113 })
114
115 - healthResp, err := client.Get("https://" + utils.HostPortOrLoopback(server.APIAddr()) + types.PathHealthz)
115 + healthResp, err := client.Get("https://" + utils.HostPortOrLoopback(server.apiListener.Addr().String()) + types.PathHealthz)
116 if err != nil {
117 t.Fatalf("GET /healthz error = %v", err)
118 }
@@ -130,7 +130,7 @@ func TestServerStartInitializesLocalACMEAndSigner(t *testing.T) {
130 t.Fatalf("GET /healthz response = %+v, want ok status", healthEnvelope)
131 }
132
133 - signResp, err := client.Get("https://" + utils.HostPortOrLoopback(server.APIAddr()) + types.PathV1Sign)
133 + signResp, err := client.Get("https://" + utils.HostPortOrLoopback(server.apiListener.Addr().String()) + types.PathV1Sign)
134 if err != nil {
135 t.Fatalf("GET /v1/sign error = %v", err)
136 }
@@ -175,7 +175,7 @@ func TestServerStartDiscoveryOmitsOwnerIdentityFields(t *testing.T) {
175 }
176 })
177
178 - resp, err := client.Get("https://" + utils.HostPortOrLoopback(server.APIAddr()) + types.PathDiscovery)
178 + resp, err := client.Get("https://" + utils.HostPortOrLoopback(server.apiListener.Addr().String()) + types.PathDiscovery)
179 if err != nil {
180 t.Fatalf("GET /discovery error = %v", err)
181 }
@@ -287,7 +287,7 @@ func TestRegisterLeaseDerivesFixedHostnameFromName(t *testing.T) {
287
288 resp, err := server.registerLease(types.RegisterChallengeRequest{
289 Name: "Demo-App",
290 - OwnerAddress: server.OwnerAddress(),
290 + OwnerAddress: server.ownerIdentity.Address,
291 }, "203.0.113.10", "")
292 if err != nil {
293 t.Fatalf("registerLease() error = %v", err)
@@ -325,7 +325,7 @@ func TestRegisterLeaseBuildsUDPEnabledRuntime(t *testing.T) {
325
326 resp, err := server.registerLease(types.RegisterChallengeRequest{
327 Name: "demo-udp",
328 - OwnerAddress: server.OwnerAddress(),
328 + OwnerAddress: server.ownerIdentity.Address,
329 UDPEnabled: true,
330 }, "203.0.113.10", "")
331 if err != nil {
@@ -597,7 +597,7 @@ func TestServerStartHidesDiscoveryRoutesWhenDisabled(t *testing.T) {
597 }
598 })
599
600 - resp, err := client.Get("https://" + utils.HostPortOrLoopback(server.APIAddr()) + types.PathDiscovery)
600 + resp, err := client.Get("https://" + utils.HostPortOrLoopback(server.apiListener.Addr().String()) + types.PathDiscovery)
601 if err != nil {
602 t.Fatalf("GET relay discovery error = %v", err)
603 }
@@ -606,7 +606,7 @@ func TestServerStartHidesDiscoveryRoutesWhenDisabled(t *testing.T) {
606 if resp.StatusCode != http.StatusNotFound {
607 t.Fatalf("GET relay discovery status = %d, want %d", resp.StatusCode, http.StatusNotFound)
608 }
609 - if server.DiscoveryEnabled() {
610 - t.Fatal("DiscoveryEnabled() = true, want false without configured discovery service")
609 + if server.cfg.DiscoveryEnabled {
610 + t.Fatal("cfg.DiscoveryEnabled = true, want false without configured discovery service")
611 }
612 }
sdk/api_client.go
+4 -15
@@ -145,25 +145,14 @@ func (a *apiClient) ensureHTTPClient(ctx context.Context) error {
145 bootstrapCtx, cancel := context.WithTimeout(ctx, defaultDialTimeout+defaultHandshakeTimeout)
146 defer cancel()
147
148 - rootCAs, err := keyless.RelayRootCAs(bootstrapCtx, a.baseURL.String(), a.baseURL.Hostname(), a.rootCAPEM)
148 + rawTLSConfig, httpClient, err := keyless.NewRelayHTTPClient(bootstrapCtx, a.baseURL, a.rootCAPEM, a.requestTimeout)
149 if err != nil {
150 return err
151 }
152 -
153 - rawTLSConfig := &tls.Config{
154 - MinVersion: tls.VersionTLS12,
155 - ServerName: a.baseURL.Hostname(),
156 - RootCAs: rootCAs,
157 - NextProtos: []string{"http/1.1"},
158 - }
159 - httpClient := &http.Client{
160 - Transport: &http.Transport{
161 - TLSClientConfig: rawTLSConfig.Clone(),
162 - ForceAttemptHTTP2: false,
163 - },
164 - Timeout: a.requestTimeout,
165 - }
152 if err := a.ensureCompatible(ctx, httpClient); err != nil {
153 + if transport, ok := httpClient.Transport.(*http.Transport); ok {
154 + transport.CloseIdleConnections()
155 + }
156 a.close()
157 return err
158 }
sdk/expose.go
+1 -3
@@ -227,8 +227,6 @@ func (e *Exposure) Close() error {
227 return closeErr
228 }
229
230 -const defaultDiscoveryInterval = 30 * time.Second
231 -
230 func (e *Exposure) runRelayDiscoveryLoop(ctx context.Context) {
231 for {
232 relayURLs := append([]string(nil), e.relaySet.ActiveRelayURLs()...)
@@ -268,7 +266,7 @@ func (e *Exposure) runRelayDiscoveryLoop(ctx context.Context) {
266 _ = e.reconcileRelayListeners(false)
267 }
268 }
271 - if !utils.SleepOrDone(ctx, defaultDiscoveryInterval) {
269 + if !utils.SleepOrDone(ctx, types.DiscoveryPollInterval) {
270 return
271 }
272 }
types/discovery.go
+2
@@ -2,6 +2,8 @@ package types
2
3 import "time"
4
5 +const DiscoveryPollInterval = 1 * time.Minute
6 +
7 type RelayDescriptor struct {
8 RelayID string `json:"relay_id"`
9