The C FFI API is the public C ABI boundary for CoQUIC. It exposes the sans-I/O Core, QUIC facade, and HTTP/3 APIs through opaque handles, fixed-width integer types, tagged structs, and explicit ownership.
Use these headers:
#include <coquic/ffi/core.h>
#include <coquic/ffi/http3.h>The implementation is packaged by TLS backend:
- CMake packages:
coquic-boringsslandcoquic-quictls - CMake shared targets:
CoQUIC::coquic_boringsslandCoQUIC::coquic_quictls - CMake static targets:
CoQUIC::coquic_boringssl_staticandCoQUIC::coquic_quictls_static - pkg-config packages:
coquic-boringsslandcoquic-quictls - static pkg-config packages:
coquic-boringssl-staticandcoquic-quictls-static
Do not link both backend packages into one process. They intentionally export the same coquic_* C symbols.
Common Rules
COQUIC_FFI_ABI_VERSION is the compile-time ABI version. Call coquic_ffi_abi_version() at runtime when a binding needs to verify that the loaded library matches the headers it was built against. The current ABI version is 2.
All API input structs with a size member must be initialized before use. Keep size at sizeof(the_struct) after initialization so future ABI versions can append fields while still accepting v1 callers.
Input buffers use coquic_bytes_t. CoQUIC copies input bytes during the call, so those buffers only need to live until the function returns.
Output buffers use coquic_bytes_view_t. These views are borrowed from the owning coquic_result_t or HTTP/3 update object. Copy data that must outlive that owner.
coquic_time_us_t is a monotonic microsecond timestamp supplied by the caller. Use one runtime clock consistently for all calls on an endpoint.
Ownership
coquic_endpoint_t owns QUIC endpoint state. Create it with coquic_endpoint_create() and release it with coquic_endpoint_destroy().
coquic_result_t owns endpoint and connection effects. Every successful endpoint, Core connection, or QUIC facade operation writes a result pointer to coquic_result_t **out_result. Release each non-null result with coquic_result_destroy() after reading its effects.
coquic_http3_client_t and coquic_http3_server_t own HTTP/3 protocol state for one QUIC connection. HTTP/3 update objects own emitted QUIC inputs and HTTP events; release them with the matching update destroy function.
Passing NULL to destroy functions is allowed.
Status And Errors
Most mutating calls return coquic_status_t:
COQUIC_STATUS_OK: the C call succeeded.COQUIC_STATUS_INVALID_ARGUMENT: a required pointer, struct size, enum tag, or index was invalid.COQUIC_STATUS_OUT_OF_MEMORY: allocation failed.COQUIC_STATUS_INTERNAL_ERROR: an unexpected implementation exception was caught before crossing the C ABI.
COQUIC_STATUS_OK does not mean the QUIC or HTTP/3 operation completed successfully at the protocol layer. Transport-level local errors are reported inside coquic_result_t; use coquic_result_has_local_error() and coquic_result_local_error().
HTTP/3 submit errors are reported with coquic_http3_error_t. Set detail_buffer and detail_buffer_capacity before the call when a binding wants a copied diagnostic string.
Event Loop
The C FFI remains sans-I/O. The caller owns sockets, timers, routing, files, threads, and scheduling.
The usual loop is:
- Create an endpoint with
coquic_endpoint_create(). - Open a client connection with
coquic_endpoint_open_connection()orcoquic_quic_connect(). - Feed inbound UDP datagrams with
coquic_endpoint_input_datagram()orcoquic_quic_receive_datagram(). - Feed timer expiry with
coquic_endpoint_timer_expired()orcoquic_quic_timer_expired(). - Feed application work with Core connection functions, QUIC facade functions, or HTTP/3-produced
coquic_connection_input_tvalues. - Iterate result effects with
coquic_result_effect_count()andcoquic_result_effect_at(). - Send every
COQUIC_EFFECT_SEND_DATAGRAMeffect through the runtime socket. - Deliver stream, DATAGRAM, lifecycle, 0-RTT, token, and diagnostic effects to the application.
- Destroy the result.
- Re-arm the runtime timer from
coquic_result_next_wakeup()orcoquic_endpoint_next_wakeup().
If coquic_result_send_continuation_pending() or coquic_endpoint_has_send_continuation_pending() returns non-zero, call back into the endpoint without blocking so queued send work can continue.
Minimal Smoke Test
#include <coquic/ffi/core.h>
int main(void) {
return coquic_ffi_abi_version() == COQUIC_FFI_ABI_VERSION ? 0 : 1;
}With pkg-config:
cc smoke.c -o smoke $(pkg-config --cflags --libs coquic-boringssl)For a static link, use the explicit static package:
cc smoke.c -o smoke $(pkg-config --cflags --libs coquic-boringssl-static)With CMake:
find_package(coquic-boringssl CONFIG REQUIRED)
target_link_libraries(app PRIVATE CoQUIC::coquic_boringssl)Use CoQUIC::coquic_boringssl_static when a static link is required.
API Reference
The usage guide intentionally keeps signatures and per-function behavior out of the main flow. See the C FFI Reference for the public function list with inputs, outputs, semantics, and important notices.
Stability
The C FFI API is intended to become the stable native binding surface. For now, treat the ABI version, exported coquic_* functions, enum values, and public struct layouts in include/coquic/ffi/core.h and include/coquic/ffi/http3.h as the compatibility boundary. Implementation files under src/ remain private.