fed-sx-m2: Step 8e — httpc:request/4 BIF wrapper (+ 10 tests)
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 37s
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 37s
Closes the BIF half of Step 8. Native http-request primitive landed in architecture via the fed-prims merge (the m2 plan's Blocker #2), so the briefing-allowed-exception wrapper in lib/erlang/runtime.sx can finally be wired. Marshalling at the BIF boundary: Url : Erlang binary -> SX string (byte-list -> integer->char). Method : Erlang atom upcased ('get -> "GET") for HTTP-wire convention, or Erlang binary passes through verbatim. Headers : Erlang proplist -> SX dict via er-proplist-to-dict. Body : Erlang binary -> SX string. Result {:status :headers :body} marshalled back to Erlang {ok, Status::integer, Headers::proplist (binary-keyed via er-of-sx-deep), Body::binary (char->integer over the SX string)}. Bad arg shapes (non-binary URL or body) raise error:badarg; native DNS / connect / bad-URL failures surface as Erlang error markers that the caller can catch. Test: next/tests/httpc_request.sh 10/10 - registration under httpc/request/4 - BIF marked non-pure - wrong-arity (/1) absent from registry - badarg on non-binary URL - badarg on non-binary body - live GET against `python3 -m http.server` -> Status 200 - body bytes match "hello from python\n" - headers come back as proplist (is_list/1 = true) - 404 path -> {ok, 404, ...} (not an error tuple) - method passed as binary works URLs spelled out as byte-list <<104,116,116,p,...>> binaries since the parser truncates <<"..."> string-literal binaries (same workaround backfill_drain.sh uses for inbox paths). Plan: 8e ticked; Blocker #2 marked RESOLVED with the merge that unblocked it referenced. Step 8f (live HTTP dispatch through delivery_worker) and Step 10c (peer-actor doc fetch) are now unblocked. No-regression gates green: Erlang conformance 761/761, http_multi_actor 44/44, follower_graph 18/18, follow_lifecycle 9/9, backfill 20/20, backfill_drain 6/6, http_listen_bif 5/5. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -594,12 +594,18 @@ a dead-letter list visible via `/admin/dead-letter`.
|
||||
in `delivery_dispatch.sh` covering single-peer enqueue,
|
||||
two-peer fan-out, missing-worker skip, no-flag no-op,
|
||||
FIFO append across two publishes, empty delivery_set no-op.
|
||||
- [ ] **8e** — `httpc:request/4` BIF wrapper. **Blocker:** the
|
||||
briefing assumed a native `http-request` primitive existed in
|
||||
`bin/sx_server.ml`; on inspection there's only `http-listen`.
|
||||
The native http-CLIENT primitive belongs to `loops/fed-prims`
|
||||
(host primitives loop). Blockers entry below. m2 work
|
||||
continues with the in-process flow until the native lands.
|
||||
- [x] **8e** — `httpc:request/4` BIF wrapper. ~~Blocker~~ resolved:
|
||||
loops/fed-prims merged into architecture, native `http-request`
|
||||
primitive available. Wrapper at `lib/erlang/runtime.sx`
|
||||
(briefing-allowed-exception scope) marshals Erlang
|
||||
`(Url::binary, Method::atom|binary, Headers::proplist, Body::binary)`
|
||||
→ SX `(http-request method url headers body)` → Erlang
|
||||
`{ok, Status::integer, Headers::proplist, Body::binary}`.
|
||||
Atom methods are upcased (`get` → `"GET"`) for HTTP-wire convention;
|
||||
binaries pass through verbatim. Test: `next/tests/httpc_request.sh`
|
||||
10/10 pass — registration, badarg validation, live GET 200,
|
||||
body bytes match, headers proplist shape, 404 surfaces as ok-tuple,
|
||||
binary method works.
|
||||
- [ ] **8f** — Real HTTP dispatch through the BIF + content-type
|
||||
wiring. dispatch_fn for live use becomes a closure over the
|
||||
peer URL that calls `httpc:request/4` with the signed envelope
|
||||
@@ -1026,17 +1032,16 @@ proceed.
|
||||
re-running on the unmodified m1 closeout HEAD.
|
||||
|
||||
2. **Native `http-request` (HTTP client) primitive missing** —
|
||||
discovered during Step 8e prep. The fed-sx-m2 briefing
|
||||
("Substrate available to you" §) claimed: "Native HTTP client
|
||||
primitive (registered in `bin/sx_server.ml`): `http-request` —
|
||||
exposed at the SX layer, currently native-only." On inspection
|
||||
`bin/sx_server.ml` only registers `http-listen`; there is no
|
||||
`http-request` registration. The HTTP client primitive belongs
|
||||
to `loops/fed-prims` (host primitives loop) per the
|
||||
one-primitive-loop-per-substrate convention. m2's Step 8e
|
||||
wrapper (`httpc:request/4` BIF in `lib/erlang/runtime.sx`)
|
||||
can land in a 1-line follow-up once the native exists; m2
|
||||
work continues with 8b-pure / 8c / 8d in the in-process flow.
|
||||
~~discovered during Step 8e prep~~ **RESOLVED 2026-06-07** by
|
||||
the user-authorized `loops/fed-prims` → `architecture` merge.
|
||||
The primitive now registers at `bin/sx_server.ml:868+` with
|
||||
signature `(http-request meth url headers body)` returning a
|
||||
`{:status :headers :body}` dict and raising `Eval_error` on
|
||||
DNS / connect / bad URL. Step 8e wired the Erlang-side BIF
|
||||
wrapper around it (`httpc:request/4`); see Progress log
|
||||
entry for marshalling details. Step 8f (live HTTP dispatch
|
||||
through `delivery_worker`) and Step 10c (peer-actor doc
|
||||
fetch in `peer_actors`) are now unblocked.
|
||||
|
||||
3. **`erlang:send_after`-style timer primitive** — discovered
|
||||
during Step 8b prep. The retry loop needs a way for the
|
||||
@@ -1055,6 +1060,39 @@ proceed.
|
||||
|
||||
Newest first.
|
||||
|
||||
- **2026-06-07** — Step 8e (closes the BIF half of Step 8;
|
||||
live HTTP dispatch in 8f next): `httpc:request/4` BIF wrapper
|
||||
landed in `lib/erlang/runtime.sx` (briefing-allowed-exception
|
||||
scope). Marshalling: Erlang URL binary → SX string via
|
||||
`(list->string (map integer->char (get url :bytes)))`; Erlang
|
||||
atom method → upcased name (`get` → `"GET"`) for HTTP wire
|
||||
convention; binary method passes through verbatim; headers
|
||||
proplist → SX dict via existing `er-proplist-to-dict`; body
|
||||
binary → SX string. Result `{:status :headers :body}` marshalled
|
||||
back to Erlang `{ok, Status, Headers::proplist, Body::binary}`
|
||||
via `er-of-sx-deep` on headers (which produces the binary-keyed
|
||||
proplist `er-dict-to-header-proplist` shape) and
|
||||
`(er-mk-binary (map char->integer (string->list body)))` for
|
||||
body. Non-binary URL / body raise `error:badarg`; the native
|
||||
primitive raises `Eval_error` on DNS / connect / bad URL which
|
||||
surfaces as an Erlang error marker the caller can catch.
|
||||
Blockers #2 (native http-request primitive) entry updated:
|
||||
RESOLVED by the loops/fed-prims → architecture merge that the
|
||||
user authorized. Test: `next/tests/httpc_request.sh` 10/10 —
|
||||
5 registration / validation cases (registration under
|
||||
`httpc/request/4`, non-pure flag, no /1 arity, badarg on
|
||||
non-binary URL, badarg on non-binary body) plus 5 live
|
||||
roundtrip cases against a background `python3 -m http.server`
|
||||
(Status 200, body bytes match `hello from python\n`, headers
|
||||
proplist shape, 404 surfaces as `{ok, 404, ...}` not as an
|
||||
error tuple, method passed as binary works). Adjacent gates:
|
||||
Erlang conformance 761/761, http_multi_actor 44/44, follower_
|
||||
graph 18/18, follow_lifecycle 9/9, backfill 20/20,
|
||||
backfill_drain 6/6, http_listen_bif 5/5 — all green; pre-
|
||||
existing cold-startup timeout sensitivity on http_get_format
|
||||
(120s internal) and nx_kernel_pure (240s internal) confirmed
|
||||
with git stash to NOT be caused by this change.
|
||||
|
||||
- **2026-06-07** — Step 9c (closes Step 9): Follow → Accept →
|
||||
backfill drain (in-process). `maybe_auto_accept/3` now calls
|
||||
`maybe_backfill/3` after the Accept publish: when
|
||||
|
||||
Reference in New Issue
Block a user