Report public gateway address for remote bridge sessions

This commit is contained in:
Haitao Pan 2026-04-10 20:57:01 +08:00
parent 19a72e32e9
commit 7f192791b5
5 changed files with 194 additions and 25 deletions

View File

@ -1,6 +1,7 @@
package acp
import (
"net/url"
"strings"
"time"
@ -52,6 +53,7 @@ func handleGatewayConnect(
Password: strings.TrimSpace(shared.StringArg(asMap(params["auth"]), "password", "")),
},
}
request.ReportedRemoteAddress = resolveGatewayReportedRemoteAddress(server, request)
result := server.gateway.Connect(request, notify)
return map[string]any{
"ok": result.OK,
@ -157,3 +159,55 @@ func parsePositiveInt(value any) int {
}
return 0
}
func resolveGatewayReportedRemoteAddress(
server *Server,
request gatewayruntime.ConnectRequest,
) string {
if strings.TrimSpace(strings.ToLower(request.Mode)) != "remote" {
return ""
}
if !shouldOverrideGatewayReportedRemoteAddress(request.Endpoint.Host) {
return ""
}
if server != nil {
if provider, ok := server.syncedProviderByID("openclaw"); ok {
if reported := publicEndpointAddressLabel(provider.Endpoint); reported != "" {
return reported
}
}
}
return publicEndpointAddressLabel(
shared.EnvOrDefault("OPENCLAW_URL", "wss://openclaw.svc.plus"),
)
}
func shouldOverrideGatewayReportedRemoteAddress(host string) bool {
switch strings.TrimSpace(strings.ToLower(host)) {
case "127.0.0.1", "localhost", "::1", "xworkmate-bridge.svc.plus":
return true
default:
return false
}
}
func publicEndpointAddressLabel(raw string) string {
parsed, err := url.Parse(strings.TrimSpace(raw))
if err != nil || strings.TrimSpace(parsed.Hostname()) == "" {
return ""
}
host := strings.TrimSpace(parsed.Hostname())
port := strings.TrimSpace(parsed.Port())
if port == "" {
switch strings.TrimSpace(strings.ToLower(parsed.Scheme)) {
case "https", "wss":
port = "443"
case "http", "ws":
port = "80"
}
}
if port == "" {
return host
}
return host + ":" + port
}

View File

@ -0,0 +1,54 @@
package acp
import (
"testing"
"xworkmate-bridge/internal/gatewayruntime"
)
func TestResolveGatewayReportedRemoteAddressUsesSyncedOpenClawEndpoint(t *testing.T) {
t.Parallel()
server := NewServer()
server.syncProviders([]syncedProvider{
{
ProviderID: "openclaw",
Label: "OpenClaw",
Endpoint: "wss://gateway.example.com",
Enabled: true,
},
})
got := resolveGatewayReportedRemoteAddress(server, gatewayruntime.ConnectRequest{
Mode: "remote",
Endpoint: gatewayruntime.Endpoint{
Host: "127.0.0.1",
Port: 18789,
TLS: false,
},
})
const want = "gateway.example.com:443"
if got != want {
t.Fatalf("resolveGatewayReportedRemoteAddress() = %q, want %q", got, want)
}
}
func TestResolveGatewayReportedRemoteAddressPreservesExplicitPublicRemoteHost(t *testing.T) {
t.Parallel()
server := NewServer()
got := resolveGatewayReportedRemoteAddress(server, gatewayruntime.ConnectRequest{
Mode: "remote",
Endpoint: gatewayruntime.Endpoint{
Host: "openclaw.svc.plus",
Port: 443,
TLS: true,
},
})
if got != "" {
t.Fatalf("expected explicit public remote host to bypass override, got %q", got)
}
}

View File

@ -292,11 +292,7 @@ func (s *session) connect() ConnectResult {
s.updateSnapshot(func(snapshot *runtimeSnapshot) {
snapshot.Status = "connecting"
snapshot.StatusText = "Connecting…"
snapshot.RemoteAddress = fmt.Sprintf(
"%s:%d",
s.config.Endpoint.Host,
s.config.Endpoint.Port,
)
snapshot.RemoteAddress = s.reportedRemoteAddress()
snapshot.LastError = ""
snapshot.LastErrorCode = ""
snapshot.LastErrorDetailCode = ""
@ -382,11 +378,7 @@ func (s *session) connectAttempt() (ConnectResult, *GatewayError) {
snapshot.Status = "connected"
snapshot.StatusText = "Connected"
snapshot.ServerName = strings.TrimSpace(stringValue(server["host"]))
snapshot.RemoteAddress = fmt.Sprintf(
"%s:%d",
s.config.Endpoint.Host,
s.config.Endpoint.Port,
)
snapshot.RemoteAddress = s.reportedRemoteAddress()
snapshot.MainSessionKey = strings.TrimSpace(
stringValue(sessionDefaults["mainSessionKey"]),
)
@ -421,6 +413,14 @@ func (s *session) connectAttempt() (ConnectResult, *GatewayError) {
}, nil
}
func (s *session) reportedRemoteAddress() string {
reported := strings.TrimSpace(s.config.ReportedRemoteAddress)
if reported != "" {
return reported
}
return fmt.Sprintf("%s:%d", s.config.Endpoint.Host, s.config.Endpoint.Port)
}
func (s *session) request(
method string,
params map[string]any,

View File

@ -0,0 +1,60 @@
package gatewayruntime
import (
"testing"
"time"
)
func TestManagerConnectUsesReportedRemoteAddressInSnapshot(t *testing.T) {
t.Parallel()
server := newFakeGatewayServer(t)
defer server.Close()
manager := NewManager()
manager.ReconnectDelay = 20 * time.Millisecond
result := manager.Connect(ConnectRequest{
RuntimeID: "runtime-1",
Mode: "remote",
ClientID: "openclaw-macos",
Locale: "en_US",
UserAgent: "XWorkmate/1.0.0",
Endpoint: Endpoint{
Host: "127.0.0.1",
Port: server.Port(),
TLS: false,
},
ReportedRemoteAddress: "openclaw.svc.plus:443",
ConnectAuthMode: "shared-token",
ConnectAuthFields: []string{"token"},
ConnectAuthSources: []string{"shared:form"},
HasSharedAuth: true,
HasDeviceToken: false,
PackageInfo: PackageInfo{
AppName: "XWorkmate",
Version: "1.0.0",
},
DeviceInfo: DeviceInfo{
Platform: "macos",
PlatformVersion: "14.0",
DeviceFamily: "Mac",
ModelIdentifier: "Mac14,5",
},
Identity: DeviceIdentity{
DeviceID: "device-1",
PublicKeyBase64URL: "tl4fnKW7VLD0Cl4lQTu2CEgHPs4PWAX7eVgWfWQWk2Q",
PrivateKeyBase64URL: "dr7GfMKoO-lJBtgA0dE5m6f_X4kEFsxChDc7mW8mkXu2Xh-cpbsUsPQKXiVBO7YISAc-zg9YBft5WBZ9ZBaTZA",
},
Auth: AuthConfig{
Token: "shared-token",
},
}, func(map[string]any) {})
if !result.OK {
t.Fatalf("expected connect success, got %#v", result.Error)
}
if got := result.Snapshot["remoteAddress"]; got != "openclaw.svc.plus:443" {
t.Fatalf("expected reported remote address, got %#v", got)
}
}

View File

@ -58,21 +58,22 @@ type AuthConfig struct {
}
type ConnectRequest struct {
RuntimeID string
Mode string
ClientID string
Locale string
UserAgent string
Endpoint Endpoint
ConnectAuthMode string
ConnectAuthFields []string
ConnectAuthSources []string
HasSharedAuth bool
HasDeviceToken bool
PackageInfo PackageInfo
DeviceInfo DeviceInfo
Identity DeviceIdentity
Auth AuthConfig
RuntimeID string
Mode string
ClientID string
Locale string
UserAgent string
Endpoint Endpoint
ReportedRemoteAddress string
ConnectAuthMode string
ConnectAuthFields []string
ConnectAuthSources []string
HasSharedAuth bool
HasDeviceToken bool
PackageInfo PackageInfo
DeviceInfo DeviceInfo
Identity DeviceIdentity
Auth AuthConfig
}
type ConnectResult struct {