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