HTTP API
Declared by <huxerui/http.h>.
Request and response values
Section titled “Request and response values”HttpMethod:Get,Head,Post,Put,Patch,Delete,Options.HttpHeader: name and value.HttpRequest: URL, method (default GET), headers, body, and optional timeout (default 30 seconds;nulloptdisables it).HttpResponse: final URL, integer status code, headers, and body.
Errors and result
Section titled “Errors and result”HttpErrorCode is Transport, Timeout, or Unsupported. HttpError contains code and message. HttpResult<T> is Result<T, HttpError>: construct from a value/error or use Success/Failure, test Succeeded(), then access Value() or Error(). Asking for the wrong alternative throws std::logic_error. Successful responses include HTTP 4xx and 5xx.
HttpClient
Section titled “HttpClient”Non-copyable root service. SendAsync(HttpRequest, progress = {}) const -> Task<HttpResult<HttpResponse>>. Obtain it with UseService<HttpClient>(). Request cancellation follows the launched HuxerUI task execution.
Complete request example
Section titled “Complete request example”#include <huxerui/huxerui.h>
using namespace huxerui;
Task<void> LoadStatus(std::shared_ptr<HttpClient> http, State<std::string> output) { auto result = co_await http->SendAsync({ .url = "https://example.com/status", .method = HttpMethod::Get, }); output = result.Succeeded() ? std::to_string(result.Value().status_code) : result.Error().message;}
[[huxerui::composable]]View LoadStatusButton(State<std::string> output) { auto http = UseService<HttpClient>(); auto tasks = UseTaskScope(); return Button("Load status").OnClick([http, tasks, output] { tasks.Launch(LoadStatus(http, output)); });}Resolve HttpClient and TaskScope during composition, then capture them in the event.
HTTP status codes are responses; transport, timeout, and unsupported-service failures use HttpError.
Streaming and progress
Section titled “Streaming and progress”SendAsync(request, progress = {}) buffers the full response. SendStreamAsync(request, progress = {}) returns Task<HttpResult<HttpResponseStream>> after final headers, before body completion. Both progress callbacks and task continuations run on the owning UI thread; do not wrap HTTP in RunWorker or post its continuation again.
HttpProgress carries kind (Upload or Download), transferred_bytes, and optional total_bytes. A missing total is unknown, not zero; avoid inventing a percentage for compressed or otherwise ambiguous content.
HttpResponseStream is move-only and owns the operation. Url(), StatusCode(), and Headers() inspect final response metadata. Body() returns its owned AsyncInputStream by reference. Await Body().ReadAsync(maximum_bytes) for IoResult<Bytes>; successful empty Bytes means EOF. Only one operation may be outstanding, including a task not yet awaited. Reads after EOF remain empty; failed/moved-from streams cannot be reused. Destroying an unfinished response cancels it.
#include <huxerui/huxerui.h>
#include <cstddef>#include <memory>#include <string>
using namespace huxerui;
Task<void> CountResponse(std::shared_ptr<HttpClient> http, State<std::string> status) { auto opened = co_await http->SendStreamAsync({.url = "https://example.com/data"}); if (!opened.Succeeded()) { status = opened.Error().message; co_return; } auto& stream = opened.Value(); if (stream.StatusCode() < 200 || stream.StatusCode() >= 300) { status = "Unexpected HTTP status"; co_return; } std::size_t received = 0; while (true) { auto read = co_await stream.Body().ReadAsync(32 * 1024); if (!read.Succeeded()) { status = read.Error().message; co_return; } if (read.Value().empty()) { status = "Complete: " + std::to_string(received) + " bytes"; co_return; } received += read.Value().size(); status = std::to_string(received) + " bytes"; }}Launch this task from an event through TaskScope. Keeping opened alive keeps its referenced stream alive, so no ownership transfer is needed. The caller chooses a positive maximum read size. Nonempty chunks can be shorter than that maximum and do not preserve UTF-8, JSON, or record boundaries. Decode incrementally or collect bytes under an application size limit. Browser requests remain subject to CORS and server policy.
Validate requests and classify outcomes
Section titled “Validate requests and classify outcomes”HttpClient validates a declaration synchronously before returning a Task. The URL must be absolute HTTP/HTTPS; GET and HEAD must not carry a body. Header names must be valid nonempty HTTP tokens, and values must not contain NUL, CR, or LF. A specified timeout must be positive. These are caller errors, not HttpErrorCode::Transport results to retry unchanged.
| Outcome | Application handling |
|---|---|
Succeeded() with expected 2xx status |
Consume/decode the body under application limits. |
| Response with 4xx/5xx status | Handle protocol/application failure; a response is not necessarily success. |
HttpErrorCode::Transport |
Connection, TLS, loading, or protocol failure before the requested result. |
HttpErrorCode::Timeout |
The complete operation exceeded its deadline. |
HttpErrorCode::Unsupported |
The adapter cannot provide this capability; choose a feature fallback. |
| Task cancellation | No normal completion or cancellation exception; the task owner updates UI policy. |
timeout covers redirects and streamed body consumption, not just opening the connection. Setting it to std::nullopt removes HuxerUI’s deadline, not platform or network failures. The final response URL can differ after redirects; do not assume it is the original origin when applying application policy.
Post-header body failures use IoError, including Timeout and Io, rather than HttpError. See Streams for terminal failure and close/copy rules. A stream can be piped into a file writer with CopyToAsync; explicitly close the writer after success.
Body and progress ownership
Section titled “Body and progress ownership”HttpRequest owns a Bytes body; encode JSON or form data and supply the corresponding Content-Type yourself. Buffered response bodies occupy memory until released. Choose SendStreamAsync for large/unknown bodies, enforce an application byte limit, and stop reading when the response is not useful. Destroying the unfinished stream cancels its remaining work.
Progress callbacks run on the UI thread. Keep them short, update only meaningful UI state, and do not perform synchronous disk writes in them. total_bytes is optional; transferred bytes are useful even when a percentage is not. Never infer a completed document from a progress value alone—wait for the buffered result or stream EOF.
Retries, authentication refresh, response parsing, and caching belong to application policy. Retry only operations safe to repeat; cancellation or a transport failure does not prove that a POST had no effect. Do not wrap HTTP in a worker or add a second callback-dispatch layer around its Task continuation.

