HTTP Client¶
Snowpulse provides an asynchronous HTTP API through AppContext::httpClient()
when SNOWPULSE_HAS_HTTP=1.
| Platform | Backend |
|---|---|
| Windows/macOS | libcurl worker thread |
Web (FULL) |
Emscripten Fetch |
| iOS | libcurl worker thread; current build is HTTP-only |
| Android | asynchronous OkHttp over JNI using the Android trust store |
Web (PLAYABLE_AD) |
Compiled out |
App calls IHttpClient::update() each active frame and dispatches completed
callbacks from there. Native network work remains off the application thread.
Requests¶
The supported methods are:
get(path, params)head(path, params)del(path, params)post(path, body, contentType)and a query-parameter overloadput(path, body, contentType)and a query-parameter overloadpatch(path, body, contentType)and a query-parameter overload
Set baseUrl() once, then pass relative paths, or pass an absolute HTTP(S) URL
to bypass the base URL.
#if SNOWPULSE_HAS_HTTP
auto* http = context()->httpClient();
http->baseUrl("https://api.example.com");
snowpulse::HttpParams params;
params.add("q", "reference");
params.add("page", 2);
params.add("includeArchived", false);
http->get("/search", params)->onComplete(
[](const snowpulse::HttpClientResult& result) {
if (result.ok()) {
std::printf("%s\n", result.body.c_str());
}
else if (!result.cancelled) {
std::fprintf(stderr, "HTTP %ld: %s\n",
result.status,
result.error.c_str());
}
});
#endif
HttpParams::add() accepts strings, C strings, booleans, integers, floats, and
doubles. Keys and values are percent-encoded and repeated entries are retained.
POST Bodies¶
Pass the serialized body and content type directly to post(). Its overload
with HttpParams appends an encoded query string while keeping the body
unchanged. The same body/content-type shape is available for put() and
patch().
#if SNOWPULSE_HAS_HTTP
auto* http = context()->httpClient();
snowpulse::HttpParams query;
query.add("validateOnly", true);
const std::string body = R"({"name":"example","enabled":true})";
http->post("/documents", query, body, "application/json")
->onComplete([](const snowpulse::HttpClientResult& result) {
if (!result.ok()) {
std::fprintf(stderr, "POST failed: %s\n", result.error.c_str());
}
});
#endif
Results¶
HttpClientResult contains:
status: HTTP status code, or0when no response code is availablebody: response bytes stored in astd::stringheaders: response headers (normalized to lowercase by libcurl; OkHttp preserves received names; the web backend currently leaves this empty)error: transport/backend error textcancelled: whether cancellation was requested
ok() is true only for a non-cancelled, error-free 2xx response.
Request Options and Cancellation¶
HttpRequest exposes header(key, value), timeout(seconds), and cancel().
Cancellation is cooperative: native libcurl requests abort through their
progress callback, Android cancels its OkHttp Call, and web Fetch reports the
request as cancelled when its completion callback returns.
header() and timeout() only change a request before its backend calls
snapshotAndMarkStarted(). The current Android backend deliberately waits for
the next update(), so fluent options set immediately after request creation
are reliable there:
#if SNOWPULSE_HAS_HTTP && defined(SNOW_PLATFORM_ANDROID)
auto* request = context()->httpClient()->post(
"/documents", R"({"name":"example"})", "application/json");
request->header("X-Client-Version", "1.0");
request->timeout(5.0f);
request->onComplete([](const snowpulse::HttpClientResult& result) {
// Inspect result.ok(), result.cancelled, status, body, and error.
});
#endif
Desktop and iOS worker threads may snapshot immediately after enqueue, so a
fluent option call can race with startup. Web snapshots synchronously inside
get()/post() before returning. Consequently, custom headers and timeouts
are not currently portable request options; the body contentType argument is
portable. This limitation does not affect onComplete() or cancel().
Requests are owned by the HTTP client. A returned HttpRequest* is valid only
until its completed callback is dispatched and the client removes it. Keep the
pointer only while the request is pending, clear it in the callback, and do not
capture an object that may be destroyed before completion.
auto* pending = context()->httpClient()->get("/documents", {});
pending->onComplete([](const snowpulse::HttpClientResult& result) {
if (result.cancelled) {
// The cancellation result has reached the application thread.
}
});
// Before completion, when the surrounding operation no longer needs it:
pending->cancel();
On Android, dispatch is deferred until the next application-thread update(), so
fluent header(), timeout(), and onComplete() calls are included before
OkHttp starts. Redirects are enabled, cleartext HTTP is rejected by default,
and TLS uses Android's system certificate store. Completion callbacks always
dispatch from the Snowpulse application thread and are dropped safely after request
or Activity destruction.