Skip to content

Multiport

Lee Yunjin edited this page Oct 7, 2026 · 1 revision

Multiport

Header: <cwist/app.h>

cwist_app_multiport() serves one app on a public port plus a list of extra TCP ports, with HTTP/3 fan-out on matching UDP ports when HTTP/3 is enabled. Any extra port can be detached into its own sub-application with its own routes, middleware, TLS and limits.

cwist_app *app = cwist_app_create();
cwist_app_get(app, "/", index_handler);

unsigned short extra[] = {8081, 9090};

/* Detach 9090 into an admin app before starting. */
cwist_app *admin = cwist_multiport_get_app(&app, 9090);
cwist_app_get(admin, "/metrics", metrics_handler);
cwist_app_use(admin, admin_auth_middleware);

int rc = cwist_app_multiport(&app, 8080, cwist_create_multiport(extra));
cwist_app_destroy(app);   /* also destroys the detached sub-apps it owns */

API

Symbol Description
cwist_multiport_t Counted port list: ports[CWIST_MULTIPORT_MAX_PORTS], count, and valid (false when construction failed, for example too many ports). CWIST_MULTIPORT_MAX_PORTS defaults to 64.
cwist_create_multiport(ports) Macro: build a descriptor from a real C array (not a pointer).
cwist_multiport_t cwist_create_multiport_from_array(const unsigned short *ports, size_t count) Build a descriptor from a pointer and length. A 0 entry stops copying.
int cwist_app_multiport(cwist_app **app_ref, unsigned short public_port, cwist_multiport_t ports) Bind the public port and every extra port, then serve until shutdown like cwist_app_listen(). Returns 0 after a graceful shutdown, -1 on a validation or bind failure.
cwist_app *cwist_multiport_get_app(cwist_app **app_ref, unsigned short port) Detach port into a sub-app (a copy of the root app's configuration at that moment) and return it. Calling it again for the same port returns the same sub-app. Returns NULL for port 0, for the public port, or on allocation failure.

Validation

cwist_app_multiport() refuses to start (and prints the reason to stderr) when:

  • public_port is 0 or the descriptor is not valid;
  • a detached sub-app's port is the public port, or is missing from the descriptor;
  • the total number of ports exceeds CWIST_MULTIPORT_MAX_PORTS;
  • an app on a port enables cleartext HTTP/2 (cwist_app_use_http2) together with HTTPS, or enables HTTPS/2 without cwist_app_use_https();
  • TLS is enabled but its context failed to initialize.

What a sub-app can tune

Detached ports are independent at the app level: routes, middleware, TLS certificate and protocol switches, error handlers, size limits and caches. Non-detached ports serve the root app; HTTP/3 settings of the root context (timeouts, push, datagrams, early data) are copied to every non-detached port's UDP listener.

Process-wide subsystems are not per-app, because they are singletons in the process:

  • cwist_full_gc(): enabling it from any app enables it for all of them.
  • The cJSON allocator hook installed at process start.
  • Environment-driven tuning (CWIST_* variables), the metrics registry and the health-probe registry.

A deployment that needs different memory-management behavior per port needs separate processes.

Ownership

  • The root app owns the sub-apps created by cwist_multiport_get_app() and destroys them in cwist_app_destroy().
  • Route and middleware contexts registered with _ex functions are shared with sub-apps without being owned by them; the app that registered them runs their destructors.

Clone this wiki locally