feat: refactor relay URL handling to support comma-separated input
yoonhyunwoo committed
Nov 17, 2025 at 17:49 UTC
337b72a8e4e0740309a421465bc36fa862ae8931
1 file changed
+23
-58
cmd/portal-tunnel/main.go
+23
-58
@@ -19,66 +19,12 @@ import (
19
20
var (
21
flagConfigPath string
22
- flagRelayURLs = newRelayListFlag("ws://localhost:4017/relay")
22
+ flagRelayURLs string
23
flagHost string
24
flagPort string
25
flagName string
26
)
27
28
-type relayListFlag struct {
29
- values []string
30
- explicit bool
31
-}
32
-
33
-func newRelayListFlag(defaults ...string) *relayListFlag {
34
- list := make([]string, 0, len(defaults))
35
- for _, value := range defaults {
36
- value = strings.TrimSpace(value)
37
- if value == "" {
38
- continue
39
- }
40
- list = append(list, value)
41
- }
42
- return &relayListFlag{values: list}
43
-}
44
-
45
-func (f *relayListFlag) Set(value string) error {
46
- if f == nil {
47
- return fmt.Errorf("relay list flag is not initialized")
48
- }
49
-
50
- if !f.explicit {
51
- f.values = nil
52
- f.explicit = true
53
- }
54
-
55
- parts := strings.Split(value, ",")
56
- for _, part := range parts {
57
- part = strings.TrimSpace(part)
58
- if part == "" {
59
- continue
60
- }
61
- f.values = append(f.values, part)
62
- }
63
- return nil
64
-}
65
-
66
-func (f *relayListFlag) String() string {
67
- if f == nil {
68
- return ""
69
- }
70
- return strings.Join(f.values, ",")
71
-}
72
-
73
-func (f *relayListFlag) Values() []string {
74
- if f == nil || len(f.values) == 0 {
75
- return nil
76
- }
77
- out := make([]string, len(f.values))
78
- copy(out, f.values)
79
- return out
80
-}
81
-
28
type serviceContext struct {
29
Name string
30
LocalAddr string
@@ -95,7 +41,7 @@ func main() {
41
case "expose":
42
fs := flag.NewFlagSet("expose", flag.ExitOnError)
43
fs.StringVar(&flagConfigPath, "config", "", "Path to portal-tunnel config file")
98
- fs.Var(flagRelayURLs, "relay", "Portal relay server URL. Repeat or comma-separate to supply multiple servers when config is not provided")
44
+ fs.StringVar(&flagRelayURLs, "relay", "ws://localhost:4017/relay", "Portal relay server URLs when config is not provided (comma-separated)")
45
fs.StringVar(&flagHost, "host", "localhost", "Local host to proxy to when config is not provided")
46
fs.StringVar(&flagPort, "port", "4018", "Local port to proxy to when config is not provided")
47
fs.StringVar(&flagName, "name", "", "Service name when config is not provided (auto-generated if empty)")
@@ -118,7 +64,7 @@ func printTunnelUsage() {
64
fmt.Println()
65
fmt.Println("Usage:")
66
fmt.Println(" portal-tunnel expose --config <file>")
121
- fmt.Println(" portal-tunnel expose [--relay URL ...] [--host HOST] [--port PORT] [--name NAME]")
67
+ fmt.Println(" portal-tunnel expose [--relay URL,URL...]] [--host HOST] [--port PORT] [--name NAME]")
68
}
69
70
func runExpose() error {
@@ -187,7 +133,7 @@ func runExposeWithConfig() error {
133
}
134
135
func runExposeWithFlags() error {
190
- relayURLs := flagRelayURLs.Values()
136
+ relayURLs := parseCommaSeparatedURLs(flagRelayURLs)
137
if len(relayURLs) == 0 {
138
return fmt.Errorf("--relay must include at least one non-empty URL when --config is not provided")
139
}
@@ -390,6 +336,25 @@ func selectServices(cfg *TunnelConfig) ([]*ServiceConfig, error) {
336
return services, nil
337
}
338
339
+func parseCommaSeparatedURLs(raw string) []string {
340
+ raw = strings.TrimSpace(raw)
341
+ if raw == "" {
342
+ return nil
343
+ }
344
+
345
+ parts := strings.Split(raw, ",")
346
+ out := make([]string, 0, len(parts))
347
+
348
+ for _, p := range parts {
349
+ p = strings.TrimSpace(p)
350
+ if p != "" {
351
+ out = append(out, p)
352
+ }
353
+ }
354
+
355
+ return out
356
+}
357
+
358
func extractHost(wsURL string) string {
359
// Simple extraction: ws://host:port/path -> host:port
360
// Remove ws:// or wss://