Skip to content

Report EINPROGRESS/EALREADY for in-flight non-blocking TCP connect#856

Open
brandonpayton wants to merge 1 commit into
mainfrom
fix/nonblock-connect-einprogress
Open

Report EINPROGRESS/EALREADY for in-flight non-blocking TCP connect#856
brandonpayton wants to merge 1 commit into
mainfrom
fix/nonblock-connect-einprogress

Conversation

@brandonpayton

Copy link
Copy Markdown
Member

Contract: non-blocking connect() must report EINPROGRESS, not EAGAIN

POSIX/Linux require a non-blocking connect() that can't complete synchronously to fail with EINPROGRESS on the first call and EALREADY while the handshake is pending. Kandelo returned EAGAIN, so the standard async-connect idiom (O_NONBLOCKconnectpoll(POLLOUT)getsockopt(SO_ERROR)) treated the connection as a hard failure.

Affects external and virtual-network TCP clients (curl, redis-cli, event-loop libraries). Loopback demos (nginx→PHP-FPM, WordPress→MariaDB) were unaffected because loopback connect completes synchronously via cross_process_loopback_connect, which is why it went unnoticed.

Fix

  • Kernel (sys_connect): the external two-phase path maps the in-flight status to EINPROGRESS on the first call (transition into Connecting) and EALREADY on subsequent calls, instead of EAGAIN. The kernel owns connect state, so it is the correct source of these errnos.
  • Host (kernel-worker.ts): the completion dispatch routes a connect returning EINPROGRESS/EALREADY — non-blocking fd → return the errno to the guest as-is; blocking fd → feed the same retry loop previously keyed on EAGAIN, so blocking connect still waits for the handshake.

ABI

No change — no new exports/imports or repr(C) types. scripts/check-abi-version.sh: snapshot in sync.

Validation

  • cargo test -p kandelo --target aarch64-apple-darwin --lib964 pass, including new test_nonblocking_connect_reports_einprogress_then_ealready, which drives sys_connect against a MockHostIO reporting an in-flight handshake and asserts EINPROGRESS then EALREADY (it returned EAGAIN twice before the fix).
  • Host npm run typecheck — clean.
  • scripts/check-abi-version.sh — snapshot in sync.

Scope note: end-to-end guest behavior follows from the kernel errno + the host routing; no prebuilt kernel wasm is available in this environment to drive a full two-process integration run, so that link is validated by construction rather than an integration test.

🤖 Generated with Claude Code

POSIX/Linux require a non-blocking `connect()` that cannot complete
synchronously to fail with `EINPROGRESS` on the first call and `EALREADY`
while the handshake is still pending. Kandelo returned `EAGAIN` instead, so the
standard async-connect idiom — `O_NONBLOCK` → `connect()` → `poll(POLLOUT)` →
`getsockopt(SO_ERROR)` — treated the connection as a hard failure. This affects
external and virtual-network TCP clients (curl, redis-cli, event-loop
libraries); loopback demos were unaffected because loopback connect completes
synchronously via `cross_process_loopback_connect`.

Kernel (`sys_connect`): the external two-phase path now maps the in-flight
status to `EINPROGRESS` on the first call (transition into `Connecting`) and
`EALREADY` on subsequent calls, instead of `EAGAIN`. The kernel owns connect
state, so it is the correct source of these errnos.

Host (`kernel-worker.ts`): the syscall-completion dispatch routes a connect that
returns `EINPROGRESS`/`EALREADY` — for a non-blocking fd it returns the errno to
the guest as-is; for a blocking fd it feeds the same retry loop previously keyed
on `EAGAIN`, so blocking connect still waits for the handshake.

No ABI change: no new exports/imports or repr(C) types; snapshot in sync.

Validation: `cargo test -p kandelo --lib` (964 pass, incl. new
`test_nonblocking_connect_reports_einprogress_then_ealready`, which drives
`sys_connect` against a host reporting an in-flight handshake and asserts
EINPROGRESS then EALREADY); host `npm run typecheck` clean;
`scripts/check-abi-version.sh` reports snapshot in sync. End-to-end guest
behavior follows from the kernel errno + the host routing (no prebuilt kernel
wasm is available in this environment to drive a full two-process integration
run).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant