A listener on all interfaces silently receives a test node's connections on macOS; the conflict canary cannot see it
@dawsontoth ci sta già lavorando.
Dal 28/9/2026.
Valutazione
Questa issue non è ancora stata valutata.
Descrizione
On macOS, a process listening on Harper's fixed ports on all interfaces, most commonly a local Harper instance running with its default config, receives a test node's connections whenever the node has no listener of its own on its loopback address. That happens before the node binds, during an HTTP-worker restart (non-overlapping on macOS), and after teardown. Clients with keep-alive, such as fetch, then stay connected to that process, so a suite fails, or passes, against the wrong server. The loopback pool's conflict canary does not detect this.
Reproduction
Machine: macOS with a Harper instance listening on *:9925, *:9926, *:1883, *:8883 (IPv6 dual-stack, started with harper run).
A suite starts a node through startHarper (it got 127.0.0.8), runs restart_service with service: 'http_workers', then sends GET /nonexistent with the harness admin credentials:
fetch(keep-alive):404 Not foundbefore the restart, then401 {"error":"Login failed"}on every later probe (20 over 10 s).- One fresh connection per probe:
404→ about 350 ms of401→404for the remaining 5 s.
lsof -nP [email protected] after the restart shows the probe connection's server end owned by the other Harper's PID, while the node is listening on 127.0.0.8:9926 again. The other instance has no admin / Abc1234! user, so it rejects the credentials. This was first investigated as a credential-store regression in Harper, because only restarts handed from a worker or job thread showed it. Those operations return before the replacement worker binds; a main-thread deploy_component with restart: true waits for it.
Why the canary misses it
findConflictingPort (src/loopbackAddressPool.ts) probes the canary ports with an exclusive bind. On macOS, Node/libuv sets SO_REUSEADDR, and BSD semantics let a bind to 127.0.0.x:P succeed next to another socket's wildcard listener on P. The bind probe therefore reports the address free. Measured with plain Node, Node 24.21.0:
| OS | wildcard listener | bind 127.0.0.1:P |
connect 127.0.0.1:P |
|---|---|---|---|
| macOS 26 (Darwin 25.6.0) | 0.0.0.0:P or [::]:P |
succeeds | accepted by the wildcard |
| Linux (node:24-slim container) | 0.0.0.0:P or [::]:P |
EADDRINUSE |
accepted by the wildcard |
When nothing else listens, a connection opened during the node's gap gets ECONNREFUSED. With a wildcard listener present, the kernel falls back to it instead.
On Linux the canary does fire, but it reports each address as still in use by another Harper node and keeps retrying with no deadline. That is a separate, lower-impact problem, tracked in #39.
Proposed fix
After the canary's bind succeeds, getNextAvailableLoopbackAddress also connect-probes the canary ports on the new address. No Harper node runs there yet, so an accepted connection means another process's listener covers the address. It then releases the slot and throws a ForeignListenerError that names the address and port, the likely cause, how to find the holder, and an opt-out: HARPER_INTEGRATION_TEST_ALLOW_FOREIGN_LISTENERS=1 turns the error into a warning. A refused loopback connect costs p50 0.03 ms / p99 0.6 ms (Linux) and 0.04 / 0.9 ms (macOS), and on Linux the bind canary catches this case first.
Out of scope, possible follow-ups:
- MQTT (1883/8883) and HTTPS (9927) are not probed. Probing them would fail every suite, MQTT or not, on a host with a resident broker; the host in #28 has one on the pool address. MQTT suites that connect across a restart (for example harper's
integrationTests/mqtt/qa649-mqtt-restart-wedge.test.ts) stay exposed to a broker listening on all interfaces. - A pre-set
ctx.harper.hostnameskips allocation, so it skips the check. - A foreign listener that starts after allocation is not detected.
Measured on
| Component | Version |
|---|---|
| @harperfast/integration-testing | 1.0.0 (origin/main 74ea958) |
| harper (system under test) | origin/main 360c60a72 |
| Node | v24.21.0 |
| OS | macOS 26, Darwin 25.6.0 (arm64); Linux via node:24-slim for the bind table |
- Lingua principale
- TypeScript
- Stelle
- 1
- Fork
- 0
- Metriche di merge delle PR
- Nessuna PR unita negli ultimi 30g
Preparare l'ambiente
Come iniziare
- Leggi tutta la issue e poi la guida ai contributi del progetto.
- Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
- Fai un fork del repository e lavora su un branch.
- Apri una pull request che faccia riferimento al numero della issue.
Altre issue di HarperFast/integration-testing
-
setupHarperWithFixture overwrites ctx.harper, dropping pre-set hostname (breaks multi-node add_node)Aperta
Difficoltà 2/5 1-3 ore Idoneità per principianti 68/100
-
enhancement good first issue
Difficoltà 2/5 1-3 ore Idoneità per principianti 85/100
-
Difficoltà 3/5 1-2 giorni Idoneità per principianti 74/100
-
Difficoltà 4/5 3-5 giorni Idoneità per principianti 58/100
-
Detached Harper children are orphaned permanently when the runner dies by SIGKILL/SIGHUP — reap guard only covers exit/SIGINT/SIGTERMForse di nuovo libera @kriszyp l’ha presa 38 giorni fa e non c’è nessuna pull request aperta. Aperta
HarperFast/integration-testing#29 · 1 commento · 1 assegnatario ·
Tutte le issue di HarperFast/integration-testing
Issue simili
-
documentation
Difficoltà 2/5 1-3 ore Idoneità per principianti 88/100
inu-appcenter/memorIN-frontend#106 ·
I maintainer di solito rispondono entro 1 giorno
-
kind/bug
Difficoltà 1/5 Meno di un'ora Idoneità per principianti 88/100
I maintainer di solito rispondono entro 7 giorni
-
[Bug] @deck.gl/arcgis dist import resolves to unpublished @deck.gl/core source path (9.3.11, 9.4.0)Aperta
Difficoltà 2/5 1-3 ore Idoneità per principianti 72/100
I maintainer di solito rispondono entro 1 giorno
-
fix: CopyFilters ignores tabApertabug
Difficoltà 2/5 1-3 ore Idoneità per principianti 82/100
CSCfi/sd-search-ui#145 ·
I maintainer di solito rispondono entro 1 giorno
-
Add: Cbeebies pl SDApertacheck:passed streams:add
Difficoltà 2/5 1-3 ore Idoneità per principianti 62/100
I maintainer di solito rispondono entro 1 giorno