Async HTTP/HTTPS Client

Main project README quick-start section: README.md

Orion provides a built-in async HTTP and HTTPS client that integrates with the window message loop. Applications issue requests with a single call and receive the response as an ordinary Orion window message — no callbacks, no polling, no curl.

Architecture

Application                 Orion kernel layer             Platform layer
──────────                  ──────────────────             ──────────────
http_request_async()  ─────►  queue request
                              worker thread ──────────────► axNetSocket / axNetConnect
                                             ──────────────► axTlsConnect (HTTPS)
                                             ──────────────► axNet/TlsSend/Recv
                              post_message(evHttpDone)
window proc ◄─────────────── dispatch_message()

The worker is a single long-lived background thread. The main thread is never blocked. HTTPS is handled transparently via the platform TLS layer (Secure Transport on macOS, OpenSSL on Linux when HAVE_OPENSSL is defined, Schannel on Windows).

Quick Start

#include "ui.h"   /* includes kernel/http.h transitively */

static result_t my_win_proc(window_t *win, uint32_t msg,
                             uint32_t wparam, void *lparam)
{
  switch (msg) {
    case evCreate:
      http_request_async(win, "https://api.example.com/data",
                         NULL, NULL);
      return true;

    case evHttpDone: {
      http_request_id_t id   = (http_request_id_t)wparam;
      http_response_t  *resp = (http_response_t *)lparam;
      if (resp->status == 200) {
        /* resp->body is a heap buffer of resp->body_len bytes */
        printf("Got %zu bytes\n", resp->body_len);
      } else if (resp->error) {
        fprintf(stderr, "Request %u failed: %s\n", id, resp->error);
      }
      http_response_free(resp);   /* transfer complete; must free */
      return true;
    }

    default:
      return false;
  }
}

Complete Example (GET + Progress + Cancel)

#include "ui.h"
#include <stdio.h>
#include <stdlib.h>

typedef struct {
  http_request_id_t active_request;
} net_demo_state_t;

static result_t net_demo_proc(window_t *win, uint32_t msg,
                               uint32_t wparam, void *lparam)
{
  net_demo_state_t *st = (net_demo_state_t *)win->userdata;

  switch (msg) {
    case evCreate:
      st = (net_demo_state_t *)calloc(1, sizeof(*st));
      if (!st) return false;
      win->userdata = st;
      st->active_request = http_request_async(win,
                                              "https://httpbin.org/bytes/200000",
                                              NULL, NULL);
      return true;

    case evHttpProgress: {
      http_progress_t *p = (http_progress_t *)lparam;
      printf("request %u progress: %zu/%zd\n",
             p->request_id, p->bytes_received, p->bytes_total);
      return true;
    }

    case evHttpDone: {
      http_request_id_t id = (http_request_id_t)wparam;
      http_response_t *resp = (http_response_t *)lparam;
      if (resp && resp->status == 200) {
        printf("request %u done: %zu bytes\n", id, resp->body_len);
      } else if (resp && resp->error) {
        printf("request %u failed: %s\n", id, resp->error);
      }
      http_response_free(resp);
      if (st && st->active_request == id)
        st->active_request = HTTP_INVALID_REQUEST;
      return true;
    }

    case evKeyDown:
      if (wparam == AX_KEY_ESCAPE && st &&
          st->active_request != HTTP_INVALID_REQUEST) {
        http_cancel(st->active_request);
        st->active_request = HTTP_INVALID_REQUEST;
        return true;
      }
      return false;

    case evDestroy:
      if (st) {
        if (st->active_request != HTTP_INVALID_REQUEST)
          http_cancel(st->active_request);
        free(st);
        win->userdata = NULL;
      }
      return true;

    default:
      return false;
  }
}

Initialisation

http_init() / http_shutdown() must bracket usage. They are called automatically when the application uses ui_init_graphics() and ui_shutdown_graphics() — standalone callers must call them explicitly.

if (!http_init()) { /* handle error */ }
/* ... */
http_shutdown();

Both functions are idempotent.

Issuing a Request

http_request_id_t http_request_async(
    window_t            *notify_win,   /* window to receive the Done message */
    const char          *url,          /* "http://…" or "https://…" */
    const http_options_t *opts,        /* NULL = defaults (GET, no body) */
    void                *userdata);    /* reserved — pass NULL */

Returns HTTP_INVALID_REQUEST (0) on immediate failure (bad URL, OOM, subsystem not initialised).

Options

typedef struct {
  http_method_t method;      /* HTTP_GET (default), HTTP_POST, HTTP_PUT, … */
  const char   *body;        /* request body bytes (NULL = none) */
  size_t        body_len;    /* 0 = treat body as null-terminated string */
  const char   *headers;     /* extra headers, each ending with \r\n */
  uint32_t      timeout_ms;  /* reserved; currently not enforced */
} http_options_t;

Example — POST with JSON body:

http_options_t opts = {
  .method   = HTTP_POST,
  .body     = "{\"name\":\"Orion\"}",
  .headers  = "Content-Type: application/json\r\n",
};
http_request_id_t id = http_request_async(win, "https://api.example.com/items",
                                           &opts, NULL);

Example — PUT with explicit body length:

static const char kPayload[] = "hello from Orion";

http_options_t opts = {
  .method   = HTTP_PUT,
  .body     = kPayload,
  .body_len = sizeof(kPayload) - 1,
  .headers  = "Content-Type: text/plain\r\n",
};

http_request_async(win, "https://api.example.com/blob/42", &opts, NULL);

Receiving the Response

evHttpDone

Posted to notify_win when the request finishes (success or failure).

Parameter Value
wparam http_request_id_t — the handle returned by http_request_async()
lparam http_response_t*caller owns; call http_response_free()
typedef struct {
  int               status;      /* HTTP status code; 0 = transport error */
  char             *body;        /* response body (not null-terminated) */
  size_t            body_len;    /* body length in bytes */
  char             *headers;     /* response header block (null-terminated) */
  const char       *error;       /* static error string, or NULL on success */
  http_request_id_t request_id;
} http_response_t;

Always call http_response_free(resp) after processing — the framework transfers ownership to the window proc.

evHttpProgress

Posted periodically during large downloads only when the server sends a Content-Length header.

Parameter Value
wparam http_request_id_t
lparam http_progress_t* — framework-owned and valid only during the message handler; do NOT retain or free
typedef struct {
  size_t            bytes_received;
  ssize_t           bytes_total;     /* -1 if Content-Length was not provided */
  http_request_id_t request_id;
} http_progress_t;

Cancellation

void http_cancel(http_request_id_t id);

Marks a pending request as cancelled. If the worker has not yet started it, no evHttpDone is posted. If the worker is already executing the request the cancellation is noted but the network I/O continues until the current read/write completes; the response is then discarded silently.

Safe to call after the request has already completed (no-op).

Current Limitations

  • timeout_ms is currently reserved and not enforced by the worker.
  • Chunked transfer decoding is not implemented yet; responses using Transfer-Encoding: chunked are returned as raw payload bytes.

Thread Safety

http_request_async() and http_cancel() are safe to call from the main thread at any time after http_init(). http_shutdown() must be called from the main thread and must not be called concurrently with http_request_async().

Redirects

Up to 8 HTTP redirects (301, 302, 303, 307, 308) are followed automatically. For 303 responses the method is changed to GET regardless of the original request method.

HTTPS

HTTPS is selected automatically when the URL scheme is https://. The platform TLS backend performs full certificate verification. There is no public API surface for TLS — it is entirely internal.


Orion UI Framework – built on SDL2 and OpenGL 3.2+

This site uses Just the Docs, a documentation theme for Jekyll.