fix: handle forward service rebind on self-occupied port

When UpdateService encounters bind address conflicts (port already in use),
the handler now automatically deletes existing forward services and retries
the AddService operation. This resolves issues where a forward's own stale
listener prevents the update.

- Add isBindAddressInUseError() to detect port bind conflicts
- Add rebindForwardServiceOnSelfOccupiedPort() for automatic cleanup and retry
- Add HasOtherForwardOnNodePort() repository method to verify port ownership
- Add unit tests for bind conflict detection
This commit is contained in:
sagitchu
2026-03-03 20:53:55 +08:00
parent 1450b25475
commit 7efb49bdab
4 changed files with 134 additions and 0 deletions
@@ -1,6 +1,7 @@
package handler
import (
"errors"
"reflect"
"testing"
)
@@ -66,6 +67,21 @@ func TestIsAlreadyExistsMessage(t *testing.T) {
}
}
func TestIsBindAddressInUseError(t *testing.T) {
if !isBindAddressInUseError(errors.New("listen tcp [::]:10001: bind: address already in use")) {
t.Fatalf("address already in use should be detected")
}
if !isBindAddressInUseError(errors.New("listen tcp4 13.228.170.187:16765: bind: cannot assign requested address")) {
t.Fatalf("cannot assign requested address should be detected")
}
if isBindAddressInUseError(errors.New("service demo already exists")) {
t.Fatalf("already exists should not be treated as bind conflict")
}
if isBindAddressInUseError(nil) {
t.Fatalf("nil error should not be treated as bind conflict")
}
}
func TestBuildForwardServiceConfigs_UsesBindIPForListen(t *testing.T) {
forward := &forwardRecord{RemoteAddr: "1.2.3.4:80", Strategy: "fifo", TunnelID: 7}
node := &nodeRecord{TCPListenAddr: "[::]", UDPListenAddr: "[::]"}