refact: remove unnecessary log, helper

Kim committed Mar 6, 2026 at 14:11 UTC c54baadde6fdb3cd53c48aad7e613002feb6f61e
7 files changed +57 -103
cmd/portal-tunnel/main.go
+6 -21
@@ -7,7 +7,6 @@ import (
7 "fmt"
8 "io"
9 "net"
10 - "net/url"
10 "os"
11 "os/signal"
12 "strings"
@@ -18,6 +17,7 @@ import (
17 "github.com/rs/zerolog"
18 "github.com/rs/zerolog/log"
19
20 + "gosuda.org/portal/portal"
21 "gosuda.org/portal/sdk"
22 )
23
@@ -68,7 +68,7 @@ func runTunnel() error {
68 if len(relayURLs) == 0 {
69 return errors.New("no relay URLs provided")
70 }
71 - relayURL, err := normalizeRelayURLsForReverseConnect(relayURLs)
71 + relayURL, err := portal.NormalizeRelayURL(relayURLs[0])
72 if err != nil {
73 return err
74 }
@@ -89,7 +89,7 @@ func runTunnel() error {
89
90 listener, err := sdkClient.Listen(ctx, sdk.ListenRequest{
91 Name: flagName,
92 - Metadata: sdk.LeaseMetadata{
92 + Metadata: portal.LeaseMetadata{
93 Description: flagDesc,
94 Tags: parseURLs(flagTags),
95 Owner: flagOwner,
@@ -172,24 +172,6 @@ loop:
172 return nil
173 }
174
175 -func normalizeRelayURLsForReverseConnect(relayURLs []string) (string, error) {
176 - raw := relayURLs[0]
177 - u, err := url.Parse(strings.TrimSpace(raw))
178 - if err != nil {
179 - return "", fmt.Errorf("parse relay url: %w", err)
180 - }
181 - if !strings.EqualFold(u.Scheme, "https") {
182 - return "", fmt.Errorf("relay url must use https: %q", raw)
183 - }
184 - if u.Host == "" {
185 - return "", fmt.Errorf("relay url host is empty: %q", raw)
186 - }
187 - u.Path = strings.TrimRight(u.Path, "/")
188 - u.RawQuery = ""
189 - u.Fragment = ""
190 - return u.String(), nil
191 -}
192 -
175 var bufferPool = sync.Pool{
176 New: func() any {
177 b := make([]byte, 64*1024)
@@ -250,6 +232,9 @@ func proxyConnection(ctx context.Context, localAddr string, relayConn net.Conn)
232 }
233
234 close(stopCh)
235 + if errors.Is(firstErr, io.EOF) || errors.Is(firstErr, net.ErrClosed) {
236 + return nil
237 + }
238 return firstErr
239 }
240
cmd/relay-server/admin.go
+13 -5
@@ -1,7 +1,9 @@
1 package main
2
3 import (
4 + "crypto/subtle"
5 "encoding/base64"
6 + "encoding/json"
7 "fmt"
8 "net/http"
9 "strings"
@@ -39,7 +41,9 @@ func (a *Admin) HandleAdminRequest(w http.ResponseWriter, r *http.Request) {
41 case "/admin":
42 a.handleAdminIndex(w)
43 case "/admin/leases":
42 - writeJSON(w, http.StatusOK, convertLeaseEntriesToRows(a.server, true, a.frontend.portalURL))
44 + w.Header().Set("Content-Type", "application/json")
45 + w.WriteHeader(http.StatusOK)
46 + _ = json.NewEncoder(w).Encode(convertLeaseEntriesToRows(a.server, true, a.frontend.portalURL))
47 default:
48 http.NotFound(w, r)
49 }
@@ -55,18 +59,22 @@ func (a *Admin) authorize(r *http.Request) bool {
59 if a.secret == "" {
60 return true
61 }
58 - if subtleValueMatch(strings.TrimSpace(r.URL.Query().Get("key")), a.secret) {
62 + queryKey := strings.TrimSpace(r.URL.Query().Get("key"))
63 + if queryKey != "" && subtle.ConstantTimeCompare([]byte(queryKey), []byte(a.secret)) == 1 {
64 return true
65 }
66 auth := strings.TrimSpace(r.Header.Get("Authorization"))
62 - if strings.HasPrefix(strings.ToLower(auth), "bearer ") && subtleValueMatch(strings.TrimSpace(auth[7:]), a.secret) {
63 - return true
67 + if strings.HasPrefix(strings.ToLower(auth), "bearer ") {
68 + bearerToken := strings.TrimSpace(auth[7:])
69 + if bearerToken != "" && subtle.ConstantTimeCompare([]byte(bearerToken), []byte(a.secret)) == 1 {
70 + return true
71 + }
72 }
73 if strings.HasPrefix(strings.ToLower(auth), "basic ") {
74 raw, err := base64.StdEncoding.DecodeString(strings.TrimSpace(auth[6:]))
75 if err == nil {
76 parts := strings.SplitN(string(raw), ":", 2)
69 - if len(parts) == 2 && subtleValueMatch(parts[1], a.secret) {
77 + if len(parts) == 2 && parts[1] != "" && subtle.ConstantTimeCompare([]byte(parts[1]), []byte(a.secret)) == 1 {
78 return true
79 }
80 }
cmd/relay-server/http_helpers.go
-24
@@ -1,34 +1,10 @@
1 package main
2
3 import (
4 - "crypto/subtle"
5 - "encoding/json"
4 "mime"
7 - "net/http"
5 "strings"
6 )
7
11 -func writeJSON(w http.ResponseWriter, status int, data any) {
12 - w.Header().Set("Content-Type", "application/json")
13 - w.WriteHeader(status)
14 - _ = json.NewEncoder(w).Encode(data)
15 -}
16 -
17 -func hasPathPrefix(path, prefix string) bool {
18 - return strings.HasPrefix(strings.TrimSpace(path), prefix)
19 -}
20 -
21 -func trimPathPrefix(path, prefix string) string {
22 - return strings.TrimPrefix(strings.TrimSpace(path), prefix)
23 -}
24 -
25 -func subtleValueMatch(left, right string) bool {
26 - if left == "" || right == "" {
27 - return false
28 - }
29 - return subtle.ConstantTimeCompare([]byte(left), []byte(right)) == 1
30 -}
31 -
8 func getContentType(ext string) string {
9 ext = strings.TrimSpace(ext)
10 if ext == "" {
cmd/relay-server/serve.go
+9 -21
@@ -3,7 +3,6 @@ package main
3 import (
4 "context"
5 "fmt"
6 - "net"
6 "net/http"
7 "os"
8 "os/signal"
@@ -56,7 +55,7 @@ func runServer(cfg relayServerConfig) error {
55 APIListenAddr: apiListenAddr,
56 SNIListenAddr: sniListenAddr,
57 RootHost: rootHost,
59 - RootFallbackAddr: loopbackAddr(apiListenAddr),
58 + RootFallbackAddr: portal.HostPortOrLoopback(apiListenAddr),
59 KeylessSignerHandler: func() http.Handler {
60 if signer == nil {
61 return nil
@@ -67,7 +66,7 @@ func runServer(cfg relayServerConfig) error {
66 CertPEM: mustRead(certFile),
67 KeyPEM: mustRead(keyFile),
68 },
70 - APIHandlerWrapper: serveAPI(frontend, admin, signer, cfg),
69 + APIHandlerWrapper: serveAPI(frontend, admin, cfg),
70 })
71 if err != nil {
72 return fmt.Errorf("create relay server: %w", err)
@@ -83,7 +82,7 @@ func runServer(cfg relayServerConfig) error {
82 defer acmeManager.Stop()
83
84 logger.Info().
86 - Str("api_addr", loopbackAddr(server.APIAddr())).
85 + Str("api_addr", portal.HostPortOrLoopback(server.APIAddr())).
86 Str("sni_addr", server.SNIAddr()).
87 Str("root_host", rootHost).
88 Bool("acme_enabled", !strings.HasSuffix(rootHost, "localhost") && rootHost != "127.0.0.1" && rootHost != "::1").
@@ -92,7 +91,7 @@ func runServer(cfg relayServerConfig) error {
91 return server.Wait()
92 }
93
95 -func serveAPI(frontend *Frontend, admin *Admin, signer *keyless.Signer, cfg relayServerConfig) func(http.Handler) http.Handler {
94 +func serveAPI(frontend *Frontend, admin *Admin, cfg relayServerConfig) func(http.Handler) http.Handler {
95 return func(base http.Handler) http.Handler {
96 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
97 switch {
@@ -104,19 +103,19 @@ func serveAPI(frontend *Frontend, admin *Admin, signer *keyless.Signer, cfg rela
103 base.ServeHTTP(w, r)
104 case isFrontendRootAssetPath(r.URL.Path):
105 frontend.ServeAsset(w, r, strings.TrimPrefix(r.URL.Path, "/"), "")
107 - case hasPathPrefix(r.URL.Path, "/assets/"):
106 + case strings.HasPrefix(strings.TrimSpace(r.URL.Path), "/assets/"):
107 frontend.ServeAsset(w, r, strings.TrimPrefix(r.URL.Path, "/"), "")
108 case r.URL.Path == "/" || r.URL.Path == "/app" || r.URL.Path == "/app/":
109 frontend.ServeAppStatic(w, r, "")
111 - case hasPathPrefix(r.URL.Path, "/app/"):
112 - frontend.ServeAppStatic(w, r, trimPathPrefix(r.URL.Path, "/app/"))
110 + case strings.HasPrefix(strings.TrimSpace(r.URL.Path), "/app/"):
111 + frontend.ServeAppStatic(w, r, strings.TrimPrefix(strings.TrimSpace(r.URL.Path), "/app/"))
112 case r.URL.Path == "/admin" || r.URL.Path == "/admin/":
113 admin.HandleAdminRequest(w, r)
115 - case hasPathPrefix(r.URL.Path, "/admin/"):
114 + case strings.HasPrefix(strings.TrimSpace(r.URL.Path), "/admin/"):
115 admin.HandleAdminRequest(w, r)
116 case r.URL.Path == "/tunnel":
117 serveTunnelScript(w, r, cfg.PortalURL)
119 - case hasPathPrefix(r.URL.Path, "/tunnel/bin/"):
118 + case strings.HasPrefix(strings.TrimSpace(r.URL.Path), "/tunnel/bin/"):
119 serveTunnelBinary(w, r)
120 default:
121 base.ServeHTTP(w, r)
@@ -140,17 +139,6 @@ func isFrontendRootAssetPath(requestPath string) bool {
139 }
140 }
141
143 -func loopbackAddr(addr string) string {
144 - host, port, err := net.SplitHostPort(addr)
145 - if err != nil {
146 - return addr
147 - }
148 - if host == "" || host == "0.0.0.0" || host == "::" {
149 - host = "127.0.0.1"
150 - }
151 - return net.JoinHostPort(host, port)
152 -}
153 -
142 func mustRead(path string) []byte {
143 if path == "" {
144 log.Fatal().Msg("missing required PEM path")
portal/helpers.go
+1 -1
@@ -139,7 +139,7 @@ func normalizeTags(tags []string) []string {
139 return out
140 }
141
142 -func hostPortOrLoopback(addr string) string {
142 +func HostPortOrLoopback(addr string) string {
143 host, port, err := net.SplitHostPort(addr)
144 if err != nil {
145 return addr
portal/server.go
+2 -2
@@ -590,7 +590,7 @@ func (s *Server) handleSNIConn(conn net.Conn) {
590
591 func (s *Server) bridgeToFallback(conn net.Conn) {
592 dialer := &net.Dialer{Timeout: 5 * time.Second}
593 - upstream, err := dialer.DialContext(s.context(), "tcp", hostPortOrLoopback(s.cfg.RootFallbackAddr))
593 + upstream, err := dialer.DialContext(s.context(), "tcp", HostPortOrLoopback(s.cfg.RootFallbackAddr))
594 if err != nil {
595 _ = conn.Close()
596 return
@@ -644,7 +644,7 @@ func (s *Server) watchContext() error {
644 func (s *Server) connectURL() string {
645 base := strings.TrimRight(s.cfg.PortalURL, "/")
646 if base == "" && s.apiListener != nil {
647 - return "https://" + hostPortOrLoopback(s.apiListener.Addr().String()) + "/sdk/connect"
647 + return "https://" + HostPortOrLoopback(s.apiListener.Addr().String()) + "/sdk/connect"
648 }
649 return base + "/sdk/connect"
650 }
sdk/client.go
+26 -29
@@ -60,12 +60,24 @@ func NewClient(cfg ClientConfig) (*Client, error) {
60 return nil, err
61 }
62
63 - cfg.DialTimeout = durationOrDefault(cfg.DialTimeout, 5*time.Second)
64 - cfg.RequestTimeout = durationOrDefault(cfg.RequestTimeout, 15*time.Second)
65 - cfg.HandshakeTimeout = durationOrDefault(cfg.HandshakeTimeout, 15*time.Second)
66 - cfg.LeaseTTL = durationOrDefault(cfg.LeaseTTL, 2*time.Minute)
67 - cfg.RenewBefore = durationOrDefault(cfg.RenewBefore, 30*time.Second)
68 - cfg.ReadyTarget = intOrDefault(cfg.ReadyTarget, 1)
63 + if cfg.DialTimeout <= 0 {
64 + cfg.DialTimeout = 5 * time.Second
65 + }
66 + if cfg.RequestTimeout <= 0 {
67 + cfg.RequestTimeout = 15 * time.Second
68 + }
69 + if cfg.HandshakeTimeout <= 0 {
70 + cfg.HandshakeTimeout = 15 * time.Second
71 + }
72 + if cfg.LeaseTTL <= 0 {
73 + cfg.LeaseTTL = 2 * time.Minute
74 + }
75 + if cfg.RenewBefore <= 0 {
76 + cfg.RenewBefore = 30 * time.Second
77 + }
78 + if cfg.ReadyTarget <= 0 {
79 + cfg.ReadyTarget = 1
80 + }
81
82 baseTLS := &tls.Config{
83 MinVersion: tls.VersionTLS12,
@@ -114,8 +126,14 @@ func (c *Client) Listen(ctx context.Context, req ListenRequest) (*Listener, erro
126 reverseToken = randomToken()
127 }
128
117 - readyTarget := intOrDefault(req.ReadyTarget, c.readyTarget)
118 - leaseTTL := durationOrDefault(req.LeaseTTL, c.leaseTTL)
129 + readyTarget := req.ReadyTarget
130 + if readyTarget <= 0 {
131 + readyTarget = c.readyTarget
132 + }
133 + leaseTTL := req.LeaseTTL
134 + if leaseTTL <= 0 {
135 + leaseTTL = c.leaseTTL
136 + }
137
138 registerReq := portal.RegisterRequest{
139 Name: req.Name,
@@ -287,27 +305,6 @@ func buildRootCAs(rootCAPEM []byte) (*x509.CertPool, error) {
305 return pool, nil
306 }
307
290 -func durationOrDefault(v, fallback time.Duration) time.Duration {
291 - if v > 0 {
292 - return v
293 - }
294 - return fallback
295 -}
296 -
297 -func intOrDefault(v, fallback int) int {
298 - if v > 0 {
299 - return v
300 - }
301 - return fallback
302 -}
303 -
304 -func max(a, b int) int {
305 - if a > b {
306 - return a
307 - }
308 - return b
309 -}
310 -
308 func randomToken() string {
309 buf := make([]byte, 8)
310 if _, err := rand.Read(buf); err != nil {