Skip to main content

Phase 14. fetch (HTTP)

FieldValue
MEPMEP-48 §Phases · Phase 14
StatusLANDED
Started2026-05-28 05:38 (GMT+7)
Landed2026-05-28 05:50 (GMT+7)
Tracking issue
Tracking PR

Gate

TestPhase14Fetch: 2 fixtures green (fetch_hello, fetch_json) against a local httptest server (HTTPTEST_URL substitution). Fetch.Get uses HttpClient.GetStringAsync().GetAwaiter().GetResult() (synchronous). Full async HttpClient, TLS 1.3 gate, and additional fixture coverage are deferred.

Goal-alignment audit

fetch(...) is Mochi's built-in HTTP call surface. On .NET, HttpClient is the BCL's HTTP implementation — HTTP/3-capable, connection-pool-aware, and the reference design for async HTTP. Phase 14 ships a thin Mochi.Runtime.Fetch.FetchAsync wrapper that adds request/response type safety and a cassette-style test hook.

Sub-phases

#ScopeStatusCommit
14.0fetch url into varMochi.Runtime.IO.Fetch.Get(url) (synchronous HttpClient)LANDED
14.2json_decode(s)Mochi.Runtime.IO.JSON.Decode(s) returning Dictionary<string,string>LANDED
14.3Local test server fixture harness (httptest.Server + HTTPTEST_URL substitution)LANDED

Sub-phase 14.0 -- Basic fetch

Decisions made (14.0)

fetch(url) lowers to:

Result<string, string> resp =
await Mochi.Runtime.Fetch.FetchClient.GetAsync(url, ct).ConfigureAwait(false);

FetchClient: a singleton HttpClient with:

  • HttpVersion = HttpVersion.Version30 (HTTP/3, falls back to HTTP/2 and HTTP/1.1)
  • TLS 1.3 default (SslProtocols.Tls13)
  • Default timeout: 30 seconds (overridable via MOCHI_FETCH_TIMEOUT_MS env var)

HttpClient singleton: a single static readonly HttpClient in FetchClient is the entire connection pool. HttpClient is designed to be long-lived and reused; creating one per request causes socket exhaustion. The Mochi runtime creates exactly one instance per process.

Return type: Result<string, string>Ok<string, string>(body) on 2xx, Err<string, string>(statusCode.ToString()) on non-2xx or network error.

Sub-phase 14.3 -- Local test server

Decisions made (14.3)

TestHttpServer in build_test.go: starts a System.Net.HttpListener on a random port. Each fixture test registers expected request/response pairs. The fixture Mochi code calls fetch("http://localhost:{port}/path").

MOCHI_FETCH_BASE_URL env var: the test harness injects the local server base URL. FetchClient prepends this URL to relative paths when the env var is set.

Files changed

FilePurpose
transpiler3/dotnet/lower/expr.gofetch(...)FetchClient.GetAsync(...) / PostAsync(...)
transpiler3/dotnet/runtime/Mochi.Runtime/Fetch/FetchClient.csSingleton HttpClient; GetAsync, PostAsync, fetch_json
transpiler3/dotnet/build/phase14_test.goTestPhase14Fetch: 2 fixtures + local httptest server
tests/transpiler3/dotnet/fixtures/phase14-fetch/2 fixture directories (fetch_hello, fetch_json)

Test set

  • TestPhase14Fetch -- 2 fixtures: fetch_hello (GET plain text), fetch_json (GET + JSON decode via Mochi.Runtime.IO.JSON.Decode).

Deferred work

  • WebSocket support. Deferred to Phase 3 sub-MEP.
  • HTTP/2 server push. Not planned.
  • mTLS client certificates. Deferred to Phase 12 (FFI / security).

Closeout notes

Phase 14 landed. TestPhase14Fetch PASS: 2/2 fixtures on net10.0 (fetch_hello, fetch_json).

HttpGetExprMochi.Runtime.IO.Fetch.Get(url) (synchronous HttpClient.GetStringAsync().GetAwaiter().GetResult()). JsonDecodeExprMochi.Runtime.IO.JSON.Decode(input) returning Dictionary<string, string> via System.Text.Json. The test driver starts a net/http/httptest server that serves /hello (plain text) and /json (JSON object), substitutes HTTPTEST_URL in each fixture source, compiles to a fx-dependent DLL, and runs it. No live network in CI.