From c6d02d9b4ab5a4b40833d01a46d4a0bc33f80f84 Mon Sep 17 00:00:00 2001 From: Antoine Toussaint <312536060+antoinetoussaint-byte@users.noreply.github.com> Date: Wed, 23 Sep 2026 15:38:44 -0400 Subject: [PATCH] fix: read the listen port from a bare host:port self endpoint Codefly hands a service its own endpoint as a bare host:port (a rendered cell writes "localhost:8080"); url.Parse reads that as scheme "localhost" with no port, so every rendered solution refused to boot with 'unresolved listen port ""'. listenPort accepts both the bare form and a URL. Co-Authored-By: Claude Opus 5.5 (1M context) --- listen_port_test.go | 21 +++++++++++++++++++++ solution.go | 22 +++++++++++++++++++--- 2 files changed, 40 insertions(+), 3 deletions(-) create mode 100644 listen_port_test.go diff --git a/listen_port_test.go b/listen_port_test.go new file mode 100644 index 0000000..75e8561 --- /dev/null +++ b/listen_port_test.go @@ -0,0 +1,21 @@ +package solution + +import "testing" + +// TestListenPortReadsEitherAddressForm pins the two shapes Codefly hands a +// service for its own endpoint: a rendered cell writes the bare host:port, a +// local run a URL. The bare form is the one url.Parse misreads as a scheme. +func TestListenPortReadsEitherAddressForm(t *testing.T) { + for address, want := range map[string]string{ + "localhost:8080": "8080", + "http://localhost:8080": "8080", + "http://127.0.0.1:41234": "41234", + "[::1]:9000": "9000", + "localhost": "", + "": "", + } { + if got := listenPort(address); got != want { + t.Errorf("listenPort(%q) = %q, want %q", address, got, want) + } + } +} diff --git a/solution.go b/solution.go index c18c341..7b9e808 100644 --- a/solution.go +++ b/solution.go @@ -256,6 +256,24 @@ func env(key, fallback string) string { // could not resolve it. Using the SDK keeps addresses and ports out of the // runtime: they come from Codefly's runtime-injected endpoint map (or, in a // local run, its deterministic native workspace map) — never a hardcoded port. +// listenPort is the port of this service's own endpoint address. Codefly hands a +// service its own endpoint as a bare "host:port" (a rendered cell writes +// "localhost:8080"), and other endpoints as URLs; url.Parse reads the bare form +// as scheme "localhost" with no port, which left every rendered solution +// refusing to boot with an empty listen port. Both forms resolve here. +func listenPort(self string) string { + if strings.Contains(self, "://") { + if u, err := url.Parse(self); err == nil { + return u.Port() + } + return "" + } + if _, port, err := net.SplitHostPort(self); err == nil { + return port + } + return "" +} + func address(ctx context.Context, module, service, endpoint, api string) string { q := codefly.For(ctx).Endpoint(endpoint) if module != "" { @@ -386,9 +404,7 @@ func loadConfig(ctx context.Context, id string) config { port := env("PORT", "") if port == "" { if self := address(ctx, "", "", "http", "http"); self != "" { - if u, err := url.Parse(self); err == nil { - port = u.Port() - } + port = listenPort(self) } } public := env("PUBLIC_URL", "")