refact: tidy helpers
Kim committed
Apr 1, 2026 at 14:41 UTC
c8270f8d80207f53b93904cfa69f5c9db30d605d
16 files changed
+157
-145
cmd/portal-tunnel/http_routes.go
+96
-81
@@ -16,9 +16,13 @@ import (
16
)
17
18
type httpRoute struct {
19
- prefix string
20
- upstream *url.URL
21
- proxy *httputil.ReverseProxy
19
+ prefix string
20
+ prefixSlash string
21
+ upstream *url.URL
22
+ upstreamPath string
23
+ upstreamPathSlash string
24
+ upstreamDomain string
25
+ proxy *httputil.ReverseProxy
26
}
27
28
func newHTTPRouteHandler(rawRoutes []string) (http.Handler, error) {
@@ -55,7 +59,7 @@ func newHTTPRouteHandler(rawRoutes []string) (http.Handler, error) {
59
p = "/"
60
}
61
for _, route := range routes {
58
- if route.prefix == "/" || p == route.prefix || strings.HasPrefix(p, route.prefix+"/") {
62
+ if route.prefix == "/" || p == route.prefix || strings.HasPrefix(p, route.prefixSlash) {
63
route.proxy.ServeHTTP(w, r)
64
return
65
}
@@ -109,22 +113,25 @@ func parseHTTPRoute(raw string) (*httpRoute, error) {
113
upstream.Fragment = ""
114
upstream.Path = utils.NormalizeURLPath(upstream.Path)
115
112
- return &httpRoute{prefix: prefix, upstream: upstream}, nil
116
+ route := &httpRoute{
117
+ prefix: prefix,
118
+ upstream: upstream,
119
+ upstreamPath: upstream.Path,
120
+ upstreamDomain: utils.NormalizeHostname(upstream.Hostname()),
121
+ }
122
+ if prefix != "/" {
123
+ route.prefixSlash = prefix + "/"
124
+ }
125
+ if upstream.Path != "/" {
126
+ route.upstreamPathSlash = upstream.Path + "/"
127
+ }
128
+ return route, nil
129
}
130
131
func (r *httpRoute) newReverseProxy() *httputil.ReverseProxy {
132
return &httputil.ReverseProxy{
117
- Rewrite: r.rewriteRequest,
118
- ModifyResponse: func(resp *http.Response) error {
119
- if resp == nil || resp.Request == nil {
120
- return nil
121
- }
122
- publicHost := resp.Request.Header.Get("X-Forwarded-Host")
123
- publicScheme := resp.Request.Header.Get("X-Forwarded-Proto")
124
- r.rewriteLocation(resp.Header, publicHost, publicScheme)
125
- r.rewriteSetCookies(resp.Header, publicHost)
126
- return nil
127
- },
133
+ Rewrite: r.rewriteRequest,
134
+ ModifyResponse: r.rewriteResponse,
135
ErrorHandler: func(w http.ResponseWriter, req *http.Request, err error) {
136
log.Error().Err(err).
137
Str("route_prefix", r.prefix).
@@ -136,28 +143,7 @@ func (r *httpRoute) newReverseProxy() *httputil.ReverseProxy {
143
}
144
145
func (r *httpRoute) rewriteRequest(pr *httputil.ProxyRequest) {
139
- // strip route prefix from the path before forwarding
140
- reqPath := utils.NormalizeURLPath(pr.In.URL.Path)
141
- rawPath := pr.In.URL.RawPath
142
- if r.prefix != "/" {
143
- if reqPath == r.prefix {
144
- reqPath = "/"
145
- rawPath = ""
146
- } else {
147
- reqPath = strings.TrimPrefix(reqPath, r.prefix)
148
- if reqPath == "" {
149
- reqPath = "/"
150
- }
151
- if rawPath == r.prefix {
152
- rawPath = "/"
153
- } else if strings.HasPrefix(rawPath, r.prefix+"/") {
154
- rawPath = strings.TrimPrefix(rawPath, r.prefix)
155
- }
156
- }
157
- }
158
-
159
- pr.Out.URL.Path = reqPath
160
- pr.Out.URL.RawPath = rawPath
146
+ pr.Out.URL.Path, pr.Out.URL.RawPath = r.publicRequestPathToUpstream(pr.In.URL.Path, pr.In.URL.RawPath)
147
pr.Out.URL.RawQuery = pr.In.URL.RawQuery
148
pr.SetURL(r.upstream)
149
pr.SetXForwarded()
@@ -176,50 +162,54 @@ func (r *httpRoute) rewriteRequest(pr *httputil.ProxyRequest) {
162
}
163
}
164
179
-func (r *httpRoute) rewriteLocation(header http.Header, publicHost, publicScheme string) {
180
- location := header.Get("Location")
181
- if location == "" {
182
- return
183
- }
184
- parsed, err := url.Parse(location)
185
- if err != nil {
186
- return
165
+func (r *httpRoute) rewriteResponse(resp *http.Response) error {
166
+ if resp == nil || resp.Request == nil {
167
+ return nil
168
}
169
189
- switch {
190
- case parsed.IsAbs():
191
- if !strings.EqualFold(parsed.Scheme, r.upstream.Scheme) || !strings.EqualFold(parsed.Host, r.upstream.Host) {
192
- return
193
- }
194
- parsed.Scheme = publicScheme
195
- parsed.Host = publicHost
196
- case strings.HasPrefix(location, "/") && parsed.Host == "" && (len(location) == 1 || (location[1] != '\\' && location[1] != '/')):
197
- // server-relative redirect
198
- default:
199
- return
200
- }
170
+ header := resp.Header
171
+ publicHost := resp.Request.Header.Get("X-Forwarded-Host")
172
+ publicScheme := resp.Request.Header.Get("X-Forwarded-Proto")
173
+
174
+ location := header.Get("Location")
175
+ if location != "" {
176
+ parsed, err := url.Parse(location)
177
+ if err == nil {
178
+ switch {
179
+ case parsed.IsAbs():
180
+ if strings.EqualFold(parsed.Scheme, r.upstream.Scheme) && strings.EqualFold(parsed.Host, r.upstream.Host) {
181
+ parsed.Scheme = publicScheme
182
+ parsed.Host = publicHost
183
+ } else {
184
+ parsed = nil
185
+ }
186
+ case strings.HasPrefix(location, "/") && parsed.Host == "" && (len(location) == 1 || (location[1] != '\\' && location[1] != '/')):
187
+ // server-relative redirect
188
+ default:
189
+ parsed = nil
190
+ }
191
202
- mapped := r.mapUpstreamPathToPublic(parsed.Path)
203
- if !strings.HasPrefix(mapped, "/") || (len(mapped) > 1 && (mapped[1] == '/' || mapped[1] == '\\')) {
204
- return
192
+ if parsed != nil {
193
+ mapped := r.upstreamPathToPublic(parsed.Path)
194
+ if strings.HasPrefix(mapped, "/") && (len(mapped) == 1 || (mapped[1] != '/' && mapped[1] != '\\')) {
195
+ parsed.Path = mapped
196
+ parsed.RawPath = ""
197
+ header.Set("Location", parsed.String())
198
+ }
199
+ }
200
+ }
201
}
206
- parsed.Path = mapped
207
- parsed.RawPath = ""
208
- header.Set("Location", parsed.String())
209
-}
202
211
-func (r *httpRoute) rewriteSetCookies(header http.Header, publicHost string) {
203
values := header.Values("Set-Cookie")
204
if len(values) == 0 {
214
- return
205
+ return nil
206
}
207
208
publicDomain := publicHost
209
if host, port, err := net.SplitHostPort(publicDomain); err == nil && port != "" {
210
publicDomain = host
211
}
221
- publicDomain = strings.ToLower(strings.Trim(publicDomain, "[]"))
222
- upstreamDomain := strings.ToLower(r.upstream.Hostname())
212
+ publicDomain = utils.NormalizeHostname(strings.Trim(publicDomain, "[]"))
213
214
header.Del("Set-Cookie")
215
for _, value := range values {
@@ -231,40 +221,65 @@ func (r *httpRoute) rewriteSetCookies(header http.Header, publicHost string) {
221
222
changed := false
223
if cookie.Path != "" {
234
- if rewritten := r.mapUpstreamPathToPublic(cookie.Path); rewritten != cookie.Path {
224
+ if rewritten := r.upstreamPathToPublic(cookie.Path); rewritten != cookie.Path {
225
cookie.Path = rewritten
226
changed = true
227
}
228
}
229
240
- domain := strings.ToLower(strings.TrimPrefix(cookie.Domain, "."))
230
+ domain := utils.NormalizeHostname(strings.TrimPrefix(cookie.Domain, "."))
231
if domain != "" && domain != publicDomain &&
242
- (domain == upstreamDomain || utils.IsLocalRelayHost(domain)) {
232
+ (domain == r.upstreamDomain || utils.IsLocalRelayHost(domain)) {
233
cookie.Domain = ""
234
changed = true
235
}
236
237
if changed {
238
header.Add("Set-Cookie", cookie.String())
249
- } else {
250
- header.Add("Set-Cookie", value)
239
+ continue
240
+ }
241
+ header.Add("Set-Cookie", value)
242
+ }
243
+
244
+ return nil
245
+}
246
+
247
+func (r *httpRoute) publicRequestPathToUpstream(path, rawPath string) (string, string) {
248
+ path = utils.NormalizeURLPath(path)
249
+ if r.prefix == "/" {
250
+ return path, rawPath
251
+ }
252
+ if path == r.prefix {
253
+ return "/", ""
254
+ }
255
+ path = strings.TrimPrefix(path, r.prefix)
256
+ if path == "" {
257
+ path = "/"
258
+ }
259
+
260
+ if rawPath != "" {
261
+ switch {
262
+ case rawPath == r.prefix:
263
+ rawPath = "/"
264
+ case strings.HasPrefix(rawPath, r.prefixSlash):
265
+ rawPath = strings.TrimPrefix(rawPath, r.prefix)
266
}
267
}
268
+ return path, rawPath
269
}
270
255
-func (r *httpRoute) mapUpstreamPathToPublic(raw string) string {
271
+func (r *httpRoute) upstreamPathToPublic(raw string) string {
272
raw = utils.NormalizeURLPath(raw)
257
- if r.prefix != "/" && (raw == r.prefix || strings.HasPrefix(raw, r.prefix+"/")) {
273
+ if r.prefix != "/" && (raw == r.prefix || strings.HasPrefix(raw, r.prefixSlash)) {
274
return raw
275
}
276
261
- base := utils.NormalizeURLPath(r.upstream.Path)
277
rest := raw
263
- if base != "/" {
264
- if raw == base {
278
+ if r.upstreamPath != "/" {
279
+ if raw == r.upstreamPath {
280
rest = "/"
266
- } else if strings.HasPrefix(raw, base+"/") {
267
- rest = strings.TrimPrefix(raw, base)
281
+ } else if strings.HasPrefix(raw, r.upstreamPathSlash) {
282
+ rest = strings.TrimPrefix(raw, r.upstreamPath)
283
}
284
}
285
cmd/relay-server/frontend.go
+1
-1
@@ -76,7 +76,7 @@ func (f *Frontend) Handler() *http.ServeMux {
76
f.ServeAppStatic(w, r, "")
77
})
78
mux.HandleFunc(types.PathAppPrefix, func(w http.ResponseWriter, r *http.Request) {
79
- f.ServeAppStatic(w, r, strings.TrimPrefix(strings.TrimSpace(r.URL.Path), types.PathAppPrefix))
79
+ f.ServeAppStatic(w, r, strings.TrimPrefix(r.URL.Path, types.PathAppPrefix))
80
})
81
mux.HandleFunc(types.PathAssetsPrefix, func(w http.ResponseWriter, r *http.Request) {
82
f.ServeAsset(w, r, strings.TrimPrefix(r.URL.Path, "/"), "")
portal/acme/route53/provider.go
+4
-6
@@ -74,7 +74,7 @@ func (p *Provider) EnsureARecords(ctx context.Context, baseDomain, publicIPv4 st
74
if p == nil {
75
return errors.New("route53 provider is nil")
76
}
77
- baseDomain = utils.NormalizeHostname(baseDomain)
77
+ baseDomain = strings.TrimPrefix(utils.NormalizeHostname(baseDomain), "*.")
78
if baseDomain == "" {
79
return errors.New("base domain is required")
80
}
@@ -211,17 +211,15 @@ func validateIPv4(raw string) error {
211
}
212
213
func domainCandidates(domain string) []string {
214
- parts := strings.Split(strings.TrimSpace(strings.TrimSuffix(domain, ".")), ".")
214
+ normalized := utils.NormalizeHostname(domain)
215
+ parts := strings.Split(normalized, ".")
216
if len(parts) < 2 {
217
return nil
218
}
219
220
candidates := make([]string, 0, len(parts)-1)
221
for i := range len(parts) - 1 {
221
- candidate := utils.NormalizeHostname(strings.Join(parts[i:], "."))
222
- if candidate != "" {
223
- candidates = append(candidates, candidate)
224
- }
222
+ candidates = append(candidates, strings.Join(parts[i:], "."))
223
}
224
return candidates
225
}
portal/api_server.go
+6
-6
@@ -143,9 +143,9 @@ func (s *Server) handleRelayDiscovery(w http.ResponseWriter, r *http.Request) {
143
ingressAddr = fmt.Sprintf("%s:%d", ingressAddr, s.cfg.SNIPort)
144
}
145
146
- supportsOverlayPeer := strings.TrimSpace(s.wgConfig.PublicKey) != "" &&
147
- strings.TrimSpace(s.wgConfig.Endpoint) != "" &&
148
- strings.TrimSpace(s.wgConfig.OverlayIPv4) != ""
146
+ supportsOverlayPeer := s.wgConfig.PublicKey != "" &&
147
+ s.wgConfig.Endpoint != "" &&
148
+ s.wgConfig.OverlayIPv4 != ""
149
150
self, err := discovery.NormalizeDescriptor(types.RelayDescriptor{
151
RelayID: s.cfg.PortalURL,
@@ -158,9 +158,9 @@ func (s *Server) handleRelayDiscovery(w http.ResponseWriter, r *http.Request) {
158
SupportsTCP: true,
159
SupportsUDP: s.cfg.UDPPortCount > 0,
160
SupportsOverlayPeer: supportsOverlayPeer,
161
- WireGuardPublicKey: strings.TrimSpace(s.wgConfig.PublicKey),
162
- WireGuardEndpoint: strings.TrimSpace(s.wgConfig.Endpoint),
163
- OverlayIPv4: strings.TrimSpace(s.wgConfig.OverlayIPv4),
161
+ WireGuardPublicKey: s.wgConfig.PublicKey,
162
+ WireGuardEndpoint: s.wgConfig.Endpoint,
163
+ OverlayIPv4: s.wgConfig.OverlayIPv4,
164
OverlayCIDRs: append([]string(nil), s.wgConfig.OverlayCIDRs...),
165
})
166
if err != nil {
portal/auth/auth.go
+5
-8
@@ -43,7 +43,7 @@ type es256kOpaqueSigner struct {
43
}
44
45
func (s *es256kOpaqueSigner) Public() *jose.JSONWebKey {
46
- return &jose.JSONWebKey{KeyID: strings.TrimSpace(s.keyID)}
46
+ return &jose.JSONWebKey{KeyID: s.keyID}
47
}
48
49
func (s *es256kOpaqueSigner) Algs() []jose.SignatureAlgorithm {
@@ -244,9 +244,7 @@ func VerifyLeaseAccessToken(token, publicKeyHex, issuer, leaseID string, now tim
244
if pubKeyText == "" {
245
return LeaseAccessTokenClaims{}, errors.New("public key is required")
246
}
247
- if strings.HasPrefix(strings.ToLower(pubKeyText), "0x") {
248
- pubKeyText = pubKeyText[2:]
249
- }
247
+ pubKeyText = utils.TrimHexPrefix(pubKeyText)
248
249
pubKeyBytes, err := hex.DecodeString(pubKeyText)
250
if err != nil {
@@ -266,7 +264,8 @@ func VerifyLeaseAccessToken(token, publicKeyHex, issuer, leaseID string, now tim
264
if err := parsed.Claims(&es256kOpaqueVerifier{publicKey: publicKey}, &claims); err != nil {
265
return LeaseAccessTokenClaims{}, err
266
}
269
- if strings.TrimSpace(leaseID) != "" && claims.LeaseID != strings.TrimSpace(leaseID) {
267
+ requestedLeaseID := strings.TrimSpace(leaseID)
268
+ if requestedLeaseID != "" && claims.LeaseID != requestedLeaseID {
269
return LeaseAccessTokenClaims{}, errors.New("lease access token lease id does not match request")
270
}
271
if err := claims.ValidateWithLeeway(jwt.Expected{
@@ -281,9 +280,7 @@ func VerifyLeaseAccessToken(token, publicKeyHex, issuer, leaseID string, now tim
280
281
func decodePrivateKeyHex(privateKeyHex string) ([]byte, error) {
282
trimmed := strings.TrimSpace(privateKeyHex)
284
- if strings.HasPrefix(strings.ToLower(trimmed), "0x") {
285
- trimmed = trimmed[2:]
286
- }
283
+ trimmed = utils.TrimHexPrefix(trimmed)
284
decoded, err := hex.DecodeString(trimmed)
285
if err != nil {
286
return nil, err
portal/discovery/discovery.go
+7
-6
@@ -98,8 +98,9 @@ func ValidateDescriptor(desc types.RelayDescriptor, now time.Time) (types.RelayD
98
}
99
100
func ValidateRelayDiscoveryResponse(resp types.DiscoveryResponse, now time.Time) (types.RelayDescriptor, []types.RelayDescriptor, error) {
101
- if strings.TrimSpace(resp.ProtocolVersion) != types.ProtocolVersion {
102
- return types.RelayDescriptor{}, nil, fmt.Errorf("relay protocol version mismatch: relay=%q client=%q", strings.TrimSpace(resp.ProtocolVersion), types.ProtocolVersion)
101
+ protocolVersion := strings.TrimSpace(resp.ProtocolVersion)
102
+ if protocolVersion != types.ProtocolVersion {
103
+ return types.RelayDescriptor{}, nil, fmt.Errorf("relay protocol version mismatch: relay=%q client=%q", protocolVersion, types.ProtocolVersion)
104
}
105
106
self, err := ValidateDescriptor(resp.Self, now)
@@ -134,7 +135,7 @@ func ValidateDescriptorTarget(desc types.RelayDescriptor, targetRelayID, targetU
135
return err
136
}
137
137
- relayID := strings.TrimSpace(normalized.RelayID)
138
+ relayID := normalized.RelayID
139
if targetRelayID != "" && relayID != targetRelayID {
140
return errors.New("descriptor relay_id does not match target relay")
141
}
@@ -205,13 +206,13 @@ func RequireOverlayRelayDescriptor(desc types.RelayDescriptor) error {
206
if !desc.SupportsOverlayPeer {
207
return errors.New("descriptor does not support overlay peer")
208
}
208
- if strings.TrimSpace(desc.WireGuardPublicKey) == "" {
209
+ if desc.WireGuardPublicKey == "" {
210
return errors.New("descriptor wireguard public key is required")
211
}
211
- if strings.TrimSpace(desc.WireGuardEndpoint) == "" {
212
+ if desc.WireGuardEndpoint == "" {
213
return errors.New("descriptor wireguard endpoint is required")
214
}
214
- if strings.TrimSpace(desc.OverlayIPv4) == "" {
215
+ if desc.OverlayIPv4 == "" {
216
return errors.New("descriptor overlay ipv4 is required")
217
}
218
return nil
portal/discovery/relayset.go
+9
-8
@@ -88,7 +88,7 @@ func (s *RelaySet) trackedRelayURLs() []string {
88
urls = append(urls, relayURL)
89
}
90
for _, view := range s.relays {
91
- relayURL := strings.TrimSpace(view.Descriptor.APIHTTPSAddr)
91
+ relayURL := view.Descriptor.APIHTTPSAddr
92
if relayURL == "" {
93
continue
94
}
@@ -237,7 +237,7 @@ func (s *RelaySet) BootstrapDescriptors() []types.RelayDescriptor {
237
continue
238
}
239
if relayID, ok := s.relayIDsByURL[relayURL]; ok {
240
- if view, ok := s.relays[relayID]; ok && strings.TrimSpace(view.Descriptor.APIHTTPSAddr) != "" {
240
+ if view, ok := s.relays[relayID]; ok && view.Descriptor.APIHTTPSAddr != "" {
241
out = append(out, view.Descriptor)
242
continue
243
}
@@ -266,9 +266,10 @@ func (s *RelaySet) BanRelayURL(relayURL, reason string) bool {
266
}
267
268
state := s.localByURL[relayURL]
269
- changed := !state.Banned || strings.TrimSpace(state.BanReason) != strings.TrimSpace(reason)
269
+ reason = strings.TrimSpace(reason)
270
+ changed := !state.Banned || strings.TrimSpace(state.BanReason) != reason
271
state.Banned = true
271
- state.BanReason = strings.TrimSpace(reason)
272
+ state.BanReason = reason
273
state.Reachable = false
274
s.localByURL[relayURL] = state
275
if changed {
@@ -387,7 +388,7 @@ func (s *RelaySet) AdvertisedDescriptors() []types.RelayDescriptor {
388
out := make([]types.RelayDescriptor, 0, len(s.relays))
389
for _, view := range s.relays {
390
state := s.localByURL[view.Descriptor.APIHTTPSAddr]
390
- if !state.Advertised || relayExpiredAt(view, state, now) || strings.TrimSpace(view.Descriptor.APIHTTPSAddr) == "" {
391
+ if !state.Advertised || relayExpiredAt(view, state, now) || view.Descriptor.APIHTTPSAddr == "" {
392
continue
393
}
394
out = append(out, view.Descriptor)
@@ -492,7 +493,7 @@ func (s *RelaySet) registerDescriptor(desc types.RelayDescriptor, now time.Time)
493
return "", false, false, err
494
}
495
if current, ok := s.relays[normalized.RelayID]; ok {
495
- currentURL := strings.TrimSpace(current.Descriptor.APIHTTPSAddr)
496
+ currentURL := current.Descriptor.APIHTTPSAddr
497
if currentURL != "" && currentURL != normalized.APIHTTPSAddr {
498
return "", false, false, errors.New("descriptor api_https_addr does not match known relay url")
499
}
@@ -523,11 +524,11 @@ func (s *RelaySet) registerDescriptor(desc types.RelayDescriptor, now time.Time)
524
525
func relayDiscoveryURLs(selfDescriptor types.RelayDescriptor, relayDescriptors []types.RelayDescriptor) []string {
526
relayURLs := make([]string, 0, 1+len(relayDescriptors))
526
- if apiURL := strings.TrimSpace(selfDescriptor.APIHTTPSAddr); apiURL != "" {
527
+ if apiURL := selfDescriptor.APIHTTPSAddr; apiURL != "" {
528
relayURLs = append(relayURLs, apiURL)
529
}
530
for _, relayDescriptor := range relayDescriptors {
530
- if apiURL := strings.TrimSpace(relayDescriptor.APIHTTPSAddr); apiURL != "" {
531
+ if apiURL := relayDescriptor.APIHTTPSAddr; apiURL != "" {
532
relayURLs = append(relayURLs, apiURL)
533
}
534
}
portal/keyless/http.go
+1
-2
@@ -6,7 +6,6 @@ import (
6
"errors"
7
"net/http"
8
"net/url"
9
- "strings"
9
"time"
10
)
11
@@ -15,7 +14,7 @@ func NewRelayHTTPClient(ctx context.Context, relayURL *url.URL, rootCAPEM []byte
14
return nil, nil, errors.New("relay url is required")
15
}
16
18
- serverName := strings.TrimSpace(relayURL.Hostname())
17
+ serverName := relayURL.Hostname()
18
if serverName == "" {
19
return nil, nil, errors.New("relay hostname is required")
20
}
portal/server.go
+1
-1
@@ -258,7 +258,7 @@ func (s *Server) Start(ctx context.Context, apiMux *http.ServeMux) error {
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) != "").
261
+ Bool("wireguard_enabled", 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 {
portal/wireguard/overlay.go
+1
-1
@@ -171,7 +171,7 @@ func peersForSnapshot(selfRelayID string, snapshot map[string]types.RelayState)
171
if desc.RelayID == selfRelayID || !desc.SupportsOverlayPeer {
172
continue
173
}
174
- if strings.TrimSpace(desc.WireGuardPublicKey) == "" || strings.TrimSpace(desc.WireGuardEndpoint) == "" || strings.TrimSpace(desc.OverlayIPv4) == "" {
174
+ if desc.WireGuardPublicKey == "" || desc.WireGuardEndpoint == "" || desc.OverlayIPv4 == "" {
175
continue
176
}
177
portal/wireguard/stack.go
+2
-2
@@ -48,7 +48,7 @@ func newStack(cfg Config) (*stack, error) {
48
return nil, err
49
}
50
51
- overlayIP, err := netip.ParseAddr(strings.TrimSpace(cfg.OverlayIPv4))
51
+ overlayIP, err := netip.ParseAddr(cfg.OverlayIPv4)
52
if err != nil || !overlayIP.Is4() {
53
return nil, errors.New("overlay ipv4 must be a valid IPv4 address")
54
}
@@ -138,7 +138,7 @@ func (s *stack) ApplyPeers(peers []types.DesiredPeer) error {
138
}
139
140
resolvedEndpoint := ""
141
- if endpoint := strings.TrimSpace(peer.WireGuardEndpoint); endpoint != "" {
141
+ if endpoint := peer.WireGuardEndpoint; endpoint != "" {
142
resolvedEndpoint, err = resolvePeerEndpoint(endpoint)
143
if err != nil {
144
s.mu.Lock()
sdk/api_client.go
+3
-2
@@ -185,8 +185,9 @@ func (a *apiClient) ensureCompatible(ctx context.Context, httpClient *http.Clien
185
}
186
return fmt.Errorf("%w: %w", errRelayIncompatible, err)
187
}
188
- if strings.TrimSpace(resp.ProtocolVersion) != types.ProtocolVersion {
189
- return fmt.Errorf("%w: relay protocol version mismatch: relay=%q client=%q", errRelayIncompatible, strings.TrimSpace(resp.ProtocolVersion), types.ProtocolVersion)
188
+ protocolVersion := strings.TrimSpace(resp.ProtocolVersion)
189
+ if protocolVersion != types.ProtocolVersion {
190
+ return fmt.Errorf("%w: relay protocol version mismatch: relay=%q client=%q", errRelayIncompatible, protocolVersion, types.ProtocolVersion)
191
}
192
return nil
193
}
sdk/listener.go
+2
-2
@@ -346,12 +346,12 @@ func (l *Listener) PublicURL() string {
346
return ""
347
}
348
349
- if strings.TrimSpace(l.api.baseURL.Scheme) == "" {
349
+ if l.api.baseURL.Scheme == "" {
350
return "https://" + hostname
351
}
352
353
host := hostname
354
- if port := strings.TrimSpace(l.api.baseURL.Port()); port != "" {
354
+ if port := l.api.baseURL.Port(); port != "" {
355
host = net.JoinHostPort(hostname, port)
356
}
357
types/api.go
+6
-4
@@ -27,11 +27,13 @@ func (e *APIRequestError) Error() string {
27
if e == nil {
28
return ""
29
}
30
- if strings.TrimSpace(e.Code) != "" {
31
- return e.Code + ": " + strings.TrimSpace(e.Message)
30
+ code := strings.TrimSpace(e.Code)
31
+ message := strings.TrimSpace(e.Message)
32
+ if code != "" {
33
+ return code + ": " + message
34
}
33
- if strings.TrimSpace(e.Message) != "" {
34
- return strings.TrimSpace(e.Message)
35
+ if message != "" {
36
+ return message
37
}
38
if e.StatusCode > 0 {
39
return fmt.Sprintf("api request failed with status %d", e.StatusCode)
utils/crypto.go
+6
-15
@@ -30,11 +30,10 @@ func NormalizeEVMAddress(raw string) (string, error) {
30
if trimmed == "" {
31
return "", errors.New("address is required")
32
}
33
- if !strings.HasPrefix(strings.ToLower(trimmed), "0x") {
33
+ hexPart := TrimHexPrefix(trimmed)
34
+ if hexPart == trimmed {
35
return "", errors.New("address must start with 0x")
36
}
36
-
37
- hexPart := trimmed[2:]
37
if len(hexPart) != 40 {
38
return "", errors.New("address must be 20 bytes")
39
}
@@ -80,9 +79,7 @@ func AddressFromCompressedPublicKeyHex(rawPublicKey string) (string, error) {
79
if publicKeyHex == "" {
80
return "", errors.New("public key is required")
81
}
83
- if strings.HasPrefix(strings.ToLower(publicKeyHex), "0x") {
84
- publicKeyHex = publicKeyHex[2:]
85
- }
82
+ publicKeyHex = TrimHexPrefix(publicKeyHex)
83
84
decoded, err := hex.DecodeString(publicKeyHex)
85
if err != nil {
@@ -182,9 +179,7 @@ func VerifySHA256Secp256k1DER(payload []byte, publicKeyHex, signatureHex string)
179
if pubKeyText == "" {
180
return errors.New("public key is required")
181
}
185
- if strings.HasPrefix(strings.ToLower(pubKeyText), "0x") {
186
- pubKeyText = pubKeyText[2:]
187
- }
182
+ pubKeyText = TrimHexPrefix(pubKeyText)
183
184
pubKeyBytes, err := hex.DecodeString(pubKeyText)
185
if err != nil {
@@ -199,9 +194,7 @@ func VerifySHA256Secp256k1DER(payload []byte, publicKeyHex, signatureHex string)
194
if sigText == "" {
195
return errors.New("signature is required")
196
}
202
- if strings.HasPrefix(strings.ToLower(sigText), "0x") {
203
- sigText = sigText[2:]
204
- }
197
+ sigText = TrimHexPrefix(sigText)
198
199
sigBytes, err := hex.DecodeString(sigText)
200
if err != nil {
@@ -224,9 +217,7 @@ func decodeSecp256k1PrivateKeyHex(raw string, requireNonZero bool) ([]byte, stri
217
if privateKeyHex == "" {
218
return nil, "", errors.New("private key is required")
219
}
227
- if strings.HasPrefix(strings.ToLower(privateKeyHex), "0x") {
228
- privateKeyHex = privateKeyHex[2:]
229
- }
220
+ privateKeyHex = TrimHexPrefix(privateKeyHex)
221
222
decoded, err := hex.DecodeString(privateKeyHex)
223
if err != nil {
utils/utils.go
+7
@@ -38,6 +38,13 @@ func SplitCSV(raw string) []string {
38
return out
39
}
40
41
+func TrimHexPrefix(raw string) string {
42
+ if len(raw) >= 2 && raw[0] == '0' && (raw[1] == 'x' || raw[1] == 'X') {
43
+ return raw[2:]
44
+ }
45
+ return raw
46
+}
47
+
48
func ParseCIDRs(raw string) ([]*net.IPNet, error) {
49
parts := SplitCSV(raw)
50
if len(parts) == 0 {