Skip to content

Internal @tcpRequest socket primitive: deferred-Text request exchange (#143) - #159

Open
assapir wants to merge 1 commit into
mainfrom
feat/tcp-request-primitive
Open

Internal @tcpRequest socket primitive: deferred-Text request exchange (#143)#159
assapir wants to merge 1 commit into
mainfrom
feat/tcp-request-primitive

Conversation

@assapir

@assapir assapir commented Aug 21, 2026

Copy link
Copy Markdown
Owner

What

Adds the low-level socket @ primitive the HTTP client (#143) will sit on:

@tcpRequest(address :: Text, requestBytes :: Text) -> Text   ~ deferred

A one-shot, close-delimited TCP request exchange — connect to address (host:port), write the request bytes, read the response until the peer closes — returning all response bytes as a deferred Text, forced on use exactly like @readStdin. This is design (a): the deferred value is raw response bytes; HTTP framing/parsing will happen in Quilon on force.

How it fits the existing model

  • Reuses, does not rebuild: it is a launch(|| ...) producer over the merged reactor-parked non-blocking TCP (quilon-rt/net.rs) and the generic launch/Deferred/force core (Deferred values + @readStdin: the value-returning half of colorless implicit futures #149). No new park/deferred plumbing.
  • A new launch_deferred_text wrapper now holds the { deferred, -1 } C-ABI tagging in one place, shared by both value-returning primitives (@readStdin, @tcpRequest).
  • The producer copies its address/request into owned buffers before the background fiber is spawned, so it never reads a caller Text that a later collection could reclaim.
  • The existing force machinery (__force_text, the deferred-taint pass) works unchanged — @tcpRequest is just recognized as a second deferred-producing primitive.

Sockets stay internal

Declared in a new trusted, internal corelib module internal.net (added to the @-declaration gate CORELIB_SOURCES), not a public core.net. The HTTP client imports it; users do not import raw sockets. No public corelib doc (it is not user-facing); noted in LANGUAGE.md's concurrency / @-primitives section as an internal primitive.

Fault handling

Fail-loud on resolve/connect/write/read failure, reporting the target address and the stage it failed at, then __exit(1) — same posture as the stdin reader.

Tests

  • Runtime round-trip (quilon-rt/net.rs): launch @tcpRequest against a local std::net listener on its own thread, force the deferred value on a fiber, assert the response bytes.
  • Compiler JIT + AOT round-trip (tests/tcp_request_test.rs): spawn a local listener, compile+run a .ql program that does one @tcpRequest and assertEqs the response — matching response exits 0, a wrong expectation trips the assertion (proving the real server bytes flowed and forced), under both quilon run (JIT) and quilon build (native AOT).
  • Extends the intrinsic-link smoke gate to cover __tcp_request_launch (guarded by a runtime-false branch so the symbol is emitted but no real connection runs).
  • Deferral-taint unit tests mirror the @readStdin ones.

Full suite passes; cargo fmt --check + clippy -D warnings clean. Reviewed via read-only correctness and simplification passes (no bugs; one duplication consolidated into launch_deferred_text).

Status

PARKED — do not merge without explicit per-PR approval.

Known limitation (documented): to_socket_addrs DNS resolution for a hostname is blocking; numeric addresses (the tests) never block. Non-blocking DNS is a later refinement.

🤖 Generated with Claude Code

…st exchange (#143)

Add `@tcpRequest(address :: Text, requestBytes :: Text) -> Text` (deferred): a
one-shot, close-delimited TCP request exchange — connect, write the request, read
the response until the peer closes — returning the response bytes as a deferred
`Text`, forced on use exactly like `@readStdin`.

It reuses the generic launch/Deferred/force core and the reactor-parked non-blocking
TCP, adding no new park/deferred plumbing: a new `launch_deferred_text` wrapper is now
shared by both value-returning primitives so the `{deferred, -1}` C-ABI tagging lives
in one place. The producer copies its address/request into owned buffers before the
background fiber is spawned, so it never reads a caller Text that could be reclaimed.

Sockets stay internal: the primitive is declared in a new trusted corelib module
`internal.net` (added to the `@`-declaration gate), not a public `core.net` — the HTTP
client sits on it and users do not import raw sockets. This is the bytes-backed
foundation for http.client's send: HTTP framing/parsing happens in Quilon on the forced
response bytes.

Ships with a runtime round-trip unit test and a compiler JIT+AOT round-trip test, both
against a local listener; extends the intrinsic-link smoke gate; documents the internal
primitive in LANGUAGE.md's concurrency section.

Co-Authored-By: Claude Opus 4.8 <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