From 7e4554942a1d098cbd1bd0f67c6f2e370cde4f36 Mon Sep 17 00:00:00 2001 From: Sarto Date: Sat, 9 May 2026 00:40:30 +0330 Subject: [PATCH] fix: update server initialization to support preferred port and persist last used port --- ios/Thefeed/ServerController.swift | 7 +++++-- mobile/mobile.go | 22 ++++++++++++++++------ mobile/mobile_test.go | 8 ++++---- 3 files changed, 25 insertions(+), 12 deletions(-) diff --git a/ios/Thefeed/ServerController.swift b/ios/Thefeed/ServerController.swift index 6e35328..92b0edf 100644 --- a/ios/Thefeed/ServerController.swift +++ b/ios/Thefeed/ServerController.swift @@ -33,13 +33,16 @@ final class ServerController: ObservableObject { guard instance == nil else { return } do { let dir = try Self.dataDir() + let saved = UserDefaults.standard.integer(forKey: "tf.lastPort") var err: NSError? - guard let s = MobileNewServer(dir.path, &err) else { + guard let s = MobileNewServer(dir.path, saved, &err) else { lastError = err?.localizedDescription ?? "server start failed" return } instance = s - port = Int(s.port()) + let actual = Int(s.port()) + port = actual + UserDefaults.standard.set(actual, forKey: "tf.lastPort") lastError = nil } catch { lastError = error.localizedDescription diff --git a/mobile/mobile.go b/mobile/mobile.go index 7d7d2ee..8a3be4c 100644 --- a/mobile/mobile.go +++ b/mobile/mobile.go @@ -7,6 +7,7 @@ package mobile import ( "errors" "net" + "strconv" "sync" "github.com/sartoopjj/thefeed/internal/web" @@ -24,15 +25,24 @@ type Server struct { done chan struct{} } -// NewServer starts a server on a kernel-assigned port. dataDir must be -// a writable, app-private directory (e.g. NSDocumentDirectory on iOS). -func NewServer(dataDir string) (*Server, error) { +// NewServer starts a server on 127.0.0.1. preferredPort=0 picks a +// kernel-assigned port; a positive value is tried first and falls +// back to kernel-assigned on bind failure. dataDir must be a writable +// app-private directory (e.g. NSDocumentDirectory on iOS). +func NewServer(dataDir string, preferredPort int) (*Server, error) { if dataDir == "" { return nil, errors.New("mobile: dataDir is empty") } - ln, err := net.Listen("tcp", "127.0.0.1:0") - if err != nil { - return nil, err + var ln net.Listener + var err error + if preferredPort > 0 { + ln, err = net.Listen("tcp", net.JoinHostPort("127.0.0.1", strconv.Itoa(preferredPort))) + } + if ln == nil { + ln, err = net.Listen("tcp", "127.0.0.1:0") + if err != nil { + return nil, err + } } port := ln.Addr().(*net.TCPAddr).Port diff --git a/mobile/mobile_test.go b/mobile/mobile_test.go index dd7ef95..bf93220 100644 --- a/mobile/mobile_test.go +++ b/mobile/mobile_test.go @@ -10,14 +10,14 @@ import ( ) func TestNewServerEmptyDir(t *testing.T) { - if _, err := NewServer(""); err == nil { + if _, err := NewServer("", 0); err == nil { t.Errorf("NewServer(\"\") succeeded, want error") } } func TestServerLifecycle(t *testing.T) { dir := t.TempDir() - s, err := NewServer(dir) + s, err := NewServer(dir, 0) if err != nil { t.Fatalf("NewServer: %v", err) } @@ -43,7 +43,7 @@ func TestServerLifecycle(t *testing.T) { } func TestStopIsIdempotent(t *testing.T) { - s, err := NewServer(t.TempDir()) + s, err := NewServer(t.TempDir(), 0) if err != nil { t.Fatalf("NewServer: %v", err) } @@ -53,7 +53,7 @@ func TestStopIsIdempotent(t *testing.T) { func TestStopReleasesPort(t *testing.T) { dir := t.TempDir() - s, err := NewServer(dir) + s, err := NewServer(dir, 0) if err != nil { t.Fatalf("NewServer: %v", err) }