C · Requests

Requests

#include <ucall/types.h>#include <ucall/server.h> 1.0.0-rc.1 · 60202332

Request handles own immutable received bytes and can outlive their callback until a reply consumes them or the server is freed. Each request can be replied to independently, including after the callback returns. ucall_server_request_path() answers the request target without its query, still percent-encoded, beside the segment and parameter accessors, and ucall_server_request_query() answers the raw query after the first ?. ucall_decode_percent() decodes the %XX escapes of either, and + as a space in a query. ucall_server_request_fields_count() and ucall_server_request_field() walk the header fields in arrival order, names lowercased and values trimmed, beside the by-name ucall_server_request_header(); responses offer the same pair. ucall_server_request_peer() reports which configured interface a request arrived on, the peer’s authenticated Noise static key, zero on plaintext interfaces, and its ucall_peer_address_t: IPv4, IPv6 with its scope, or AF_UNIX. Multipart bodies are read part by part through ucall_server_request_parts_count() and ucall_server_request_part(). A reply to a disconnected request reports connection closure and consumes the handle. All byte views carry an explicit length, including empty and binary values.

API

Types

ucall_byte_tOne byte of raw storage shared by views and cryptographic keys.
types.h:40 ↗
typedef uint8_t ucall_byte_t;
ucall_bytes_view_tA borrowed run of bytes; text is bytes that happen to decode.
types.h:45 ↗
struct ucall_bytes_view_t

Empty is {NULL, 0}. Length never requests a null-terminator lookup.

Fields

dataucall_byte_t const *First byte of the run.
lengthsize_tBytes the run spans, with no terminator assumed or written.
ucall_received_http_request_tOpaque handle for a single Remote Procedure Call on the server side.
types.h:159 ↗
typedef struct ucall_received_http_request_handle_t * ucall_received_http_request_t;
ucall_address_family_tThe family of a peer's socket address.
types.h:423 ↗
enum ucall_address_family_t

Values

ucall_address_family_unknown_k0No address was read for this peer.
ucall_address_family_ipv4_k1IPv4, in the first 4 bytes.
ucall_address_family_ipv6_k2IPv6, in all 16 bytes, with its scope.
ucall_address_family_unix_k3A local AF_UNIX peer, with no address bytes.
ucall_peer_address_tA peer's socket address: IPv4 in the first 4 bytes, IPv6 in all 16, both in network order, the port in host order and the IPv6 scope; AF_UNIX peers carry only the family.
types.h:440 ↗
struct ucall_peer_address_t

Fields

bytesucall_byte_tThe address in network order: IPv4 in the first 4 bytes, IPv6 in all 16.
scopeuint32_tThe IPv6 scope, 0 otherwise.
portuint16_tThe port in host order.
familyuint8_tA ucall_address_family_t, one byte wide.
reserveduint8_tZero, keeping the record at 24 bytes.
ucall_http_status_tHTTP status codes for server responses and client results.
types.h:462 ↗
typedef uint32_t ucall_http_status_t;

Numeric codes also represent responses absent from the named constants below. For library outcomes, use ucall_status_t instead.

Values

ucall_http_status_ok_k200Request succeeded.
ucall_http_status_created_k201Resource created.
ucall_http_status_no_content_k204Success with an empty body.
ucall_http_status_partial_content_k206Success with the requested Range of the body.
ucall_http_status_moved_permanently_k301Resource has a new permanent URL.
ucall_http_status_found_k302Resource is temporarily at a different URL.
ucall_http_status_not_modified_k304Cached copy is still fresh.
ucall_http_status_bad_request_k400Malformed request syntax.
ucall_http_status_unauthorized_k401Authentication required or failed.
ucall_http_status_forbidden_k403Authenticated, but not permitted.
ucall_http_status_not_found_k404No endpoint matches the request path.
ucall_http_status_method_not_allowed_k405Endpoint exists, but not for this verb.
ucall_http_status_timeout_k408Client took too long to send the request.
ucall_http_status_conflict_k409Request conflicts with current resource state.
ucall_http_status_precondition_failed_k412A request condition does not match the current resource.
ucall_http_status_payload_too_large_k413Request body is too large.
ucall_http_status_range_not_satisfiable_k416A requested byte range starts or ends past the end of the resource.
ucall_http_status_too_many_requests_k429Rate limit exceeded.
ucall_http_status_internal_error_k500Unhandled failure inside an endpoint.
ucall_http_status_not_implemented_k501Verb or feature not supported.
ucall_http_status_bad_gateway_k502An upstream answered with something no HTTP peer would send.
ucall_http_status_service_unavailable_k503Server is overloaded or shutting down.
ucall_http_status_gateway_timeout_k504Upstream dependency timed out.
ucall_http_status_version_not_supported_k505Request names an HTTP version the server does not speak.
ucall_percent_mode_tWhich part of a URL ucall_decode_percent() reads, as only a query spells a space '+'.
types.h:621 ↗
enum ucall_percent_mode_t

Values

ucall_percent_path_k0A path, where '+' stays itself.
ucall_percent_query_k1A query, where '+' is a space.
ucall_received_http_request_callback_tCallback type for handling incoming requests on the server side.
server.h:489 ↗
typedef void(*) ucall_received_http_request_callback_t(ucall_received_http_request_t request, void *context);
ucall_reply_part_kind_tWhat one part of a reply body holds.
server.h:1166 ↗
enum ucall_reply_part_kind_t

Values

ucall_reply_part_bytes_k0data.bytes, copied before the call returns.
ucall_reply_part_file_k1data.file, a range of a descriptor the event loop reads unbuffered and sends without copying where the kernel allows.
ucall_reply_part_path_k2data.path, a range of a file the event loop opens beneath a registered directory.
ucall_reply_part_tOne part of a reply body: bytes in memory, or a byte range of a file, which the event loop reads after the call returns.
server.h:1218 ↗
struct ucall_reply_part_t

Fields

kinducall_reply_part_kind_tWhich member of data the part uses.
dataunion ucall_reply_part_t::@263277132012061031073105306216242353260173061112

Functions

ucall_decode_percentDecodes the XX escapes of encoded, and '+' as a space in query mode.
types.h:644 ↗
ucall_status_t ucall_decode_percent(
    ucall_bytes_view_t encoded,
    ucall_byte_t *decoded,
    size_t *length,
    ucall_percent_mode_t mode);

Parameters

encodedinA path or a query value, as a request lends it.
decodedoutAt least encoded.length bytes; may alias encoded.data.
lengthoutThe decoded length.
modeinWhether a '+' is itself or a space.

Returns

The decoding's status.

Notes

Thread-safe.

Statuses

  • ucall_status_invalid_argument_k A '' without two hexadecimal digits, an unknown mode, or a missing length, decoded or encoded.data while encoded.length is not zero.
ucall_server_request_verbReturns the HTTP method of the received request.
server.h:636 ↗
ucall_verb_t ucall_server_request_verb(
    ucall_server_t server,
    ucall_received_http_request_t request);

Never ucall_verb_any_k.

Needed by endpoints registered with ucall_verb_any_k, which match every method.

Parameters

serverinServer handle from ucall_server_init().
requestinThe received request inside the ucall_received_http_request_callback_t.

Notes

Thread-safe across requests. Two threads must not operate on the same request concurrently.

ucall_server_request_pathExtracts the path of the received request: its target without the query.
server.h:668 ↗
void ucall_server_request_path(ucall_server_request_path_t *arguments);

The path stays as the peer sent it, percent-encoded. Routing and the segment accessors take it exactly: / holds no segments, every / after the first starts one, so //a holds an empty segment before a and a trailing slash adds an empty last one.

Parameters

serverucall_server_tThe server this call operates on.
outcomeucall_outcome_t *Output for the outcome.
requestucall_received_http_request_tThe received request inside the ucall_received_http_request_callback_t.
pathucall_bytes_view_t *Output for a borrowed view of the path.

Notes

Thread-safe across requests, though two threads must not use one request at once.

Statuses

ucall_server_request_queryExtracts the query of the received request: its target after the first ?
server.h:697 ↗
void ucall_server_request_query(
    ucall_server_request_query_t *arguments);

, as sent and percent-encoded, or empty when it has none.

Parameters

serverucall_server_tThe server this call operates on.
outcomeucall_outcome_t *The verb's status, listed on the verb.
requestucall_received_http_request_tThe received request inside the ucall_received_http_request_callback_t.
queryucall_bytes_view_t *Output for a borrowed view of the query.

Notes

Thread-safe across requests, though two threads must not use one request at once.

Statuses

ucall_server_request_path_segments_countReturns the number of path components of the received request.
server.h:707 ↗
size_t ucall_server_request_path_segments_count(
    ucall_server_t server,
    ucall_received_http_request_t request);

Parameters

serverinServer handle from ucall_server_init().
requestinThe received request inside the ucall_received_http_request_callback_t.

Notes

Thread-safe across requests. Two threads must not operate on the same request concurrently.

ucall_server_request_path_segmentExtracts an indexed path component from the received request.
server.h:742 ↗
void ucall_server_request_path_segment(
    ucall_server_request_path_segment_t *arguments);

Useful for RESTful APIs with path parameters, e.g., "/users/{id}/posts/{postId}".

Parameters

serverucall_server_tThe server this call operates on.
outcomeucall_outcome_t *The verb's status, listed on the verb.
requestucall_received_http_request_tThe received request inside the ucall_received_http_request_callback_t.
indexsize_tThe zero-based index of the path component to extract.
segmentucall_bytes_view_t *Output for a borrowed view of the path component.

Notes

Thread-safe across requests, though two threads must not use one request at once.

Statuses

ucall_server_request_path_parameterExtracts a named path parameter captured by the endpoint's URI template.
server.h:779 ↗
void ucall_server_request_path_parameter(
    ucall_server_request_path_parameter_t *arguments);

For an endpoint registered as "/users/{id}/posts/{post_id}" receiving "/users/7/posts/3", a name of "id" yields "7". A greedy wildcard {*path} captures the remaining segments including separators.

Parameters

serverucall_server_tThe server this call operates on.
outcomeucall_outcome_t *The verb's status, listed on the verb.
requestucall_received_http_request_tThe received request inside the ucall_received_http_request_callback_t.
nameucall_bytes_view_tThe case-sensitive placeholder name, without braces.
valueucall_bytes_view_t *Output for a borrowed view of the captured value.

Notes

Thread-safe across requests, though two threads must not use one request at once.

Statuses

ucall_server_request_query_parameterExtracts the named string parameter from the received request.
server.h:815 ↗
void ucall_server_request_query_parameter(
    ucall_server_request_query_parameter_t *arguments);

Unlike HTTP headers, query parameters are case-sensitive by specification, but server-dependent in practice. An example would be ASP.NET / IIS, which treats "?id" and "?ID" as same parameters.

Parameters

serverucall_server_tThe server this call operates on.
outcomeucall_outcome_t *The verb's status, listed on the verb.
requestucall_received_http_request_tThe received request inside the ucall_received_http_request_callback_t.
nameucall_bytes_view_tThe case-sensitive name of the parameter to extract.
valueucall_bytes_view_t *Output for a borrowed view of the parameter value.

Notes

Thread-safe across requests, though two threads must not use one request at once.

Statuses

ucall_server_request_headerExtracts the named string header from the received request.
server.h:848 ↗
void ucall_server_request_header(
    ucall_server_request_header_t *arguments);

Parameters

serverucall_server_tThe server this call operates on.
outcomeucall_outcome_t *The verb's status, listed on the verb.
requestucall_received_http_request_tThe received request inside the ucall_received_http_request_callback_t.
nameucall_bytes_view_tThe name of the HTTP header to extract (case-insensitive).
valueucall_bytes_view_t *Output for a borrowed view of the header value.

Notes

Thread-safe across requests, though two threads must not use one request at once.

Statuses

ucall_server_request_fields_countCounts the header field lines of the received request, repeated names included.
server.h:876 ↗
void ucall_server_request_fields_count(
    ucall_server_request_fields_count_t *arguments);

Parameters

serverucall_server_tThe server this call operates on.
outcomeucall_outcome_t *The verb's status, listed on the verb.
requestucall_received_http_request_tThe received request inside the ucall_received_http_request_callback_t.
countsize_t *Output for the number of header field lines, in arrival order.

Notes

Thread-safe across requests, though two threads must not use one request at once.

Statuses

ucall_server_request_fieldExtracts one header field line of the received request by its arrival order, as a proxy forwards a head whole.
server.h:913 ↗
void ucall_server_request_field(
    ucall_server_request_field_t *arguments);

Parameters

serverucall_server_tThe server this call operates on.
outcomeucall_outcome_t *The verb's status, listed on the verb.
requestucall_received_http_request_tThe received request inside the ucall_received_http_request_callback_t.
indexsize_tWhich field line, below ucall_server_request_fields_count().
nameucall_bytes_view_t *Output for a borrowed view of its name, lowercased as the parser keeps every field name.
valueucall_bytes_view_t *Output for a borrowed view of its value, without surrounding whitespace.

Notes

Thread-safe across requests, though two threads must not use one request at once.

Statuses

ucall_server_request_peerReports which interface a request arrived on, which authenticated key sent it, and from which socket address.
server.h:951 ↗
void ucall_server_request_peer(ucall_server_request_peer_t *arguments);

All three are copied when the request is received, so they remain readable after its connection closes, as batched requests often outlive theirs.

Parameters

serverucall_server_tThe server this call operates on.
outcomeucall_outcome_t *Output for the outcome.
requestucall_received_http_request_tA request from any endpoint, plain or batch, until its reply.
network_interface_indexsize_t *Output for the position of its interface in ucall_server_options_t::interfaces.
static_keyucall_noise_key_t *Output for the peer's authenticated Noise static key; zeroed on plaintext interfaces.
addressucall_peer_address_t *Output for the peer's socket address as accepted.

Notes

Thread-safe across requests, though two threads must not use one request at once.

Statuses

ucall_server_request_bodyExtracts the body content from the received request.
server.h:982 ↗
void ucall_server_request_body(ucall_server_request_body_t *arguments);

A streamed endpoint's body is pulled through ucall_server_request_body_chunk() instead.

Parameters

serverucall_server_tThe server this call operates on.
outcomeucall_outcome_t *The verb's status, listed on the verb.
requestucall_received_http_request_tThe received request inside the ucall_received_http_request_callback_t.
bodyucall_bytes_view_t *Output for a borrowed view of the body.

Notes

Thread-safe across requests, though two threads must not use one request at once.

Statuses

ucall_server_request_body_chunkPulls the next chunk of a request body that is still arriving.
server.h:1027 ↗
void ucall_server_request_body_chunk(
    ucall_server_request_body_chunk_t *arguments);

Endpoints registered with ucall_body_delivery_streamed_k are invoked as soon as headers are parsed, before the body is complete. Without this, a multi-gigabyte upload must be buffered in full before the handler runs, which is both a memory hazard and a denial-of-service vector. Each pull takes every byte decoded since the last one; the connection reads on only once the previous bytes were pulled, and parses no pipelined request until the body ended. On a buffered endpoint the first pull answers the whole body and the next one empty.

Parameters

serverucall_server_tThe server this call operates on.
outcomeucall_outcome_t *Final with the next bytes, or deferred until they arrive; a deferred pull finishes on the request's polling thread, so a pull from that thread with nothing decoded yet answers ucall_status_would_block_k instead and keeps no outcome.
requestucall_received_http_request_tThe received request inside the ucall_received_http_request_callback_t.
dataucall_bytes_view_t *Output for a borrowed view of the bytes decoded since the last pull, valid until the next pull and written once the outcome is final; empty once the body ended.

Warnings

The returned data is valid only until the next call on this request.

See also

Use ucall_server_request_body() for endpoints that are not registered as streaming.

Notes

Thread-safe across requests, though two threads must not use one request at once.

Statuses

ucall_server_request_parts_countReturns the number of parts in a multipart/form-data request body.
server.h:1038 ↗
size_t ucall_server_request_parts_count(
    ucall_server_t server,
    ucall_received_http_request_t request);

Zero if it is not multipart.

Parameters

serverinServer handle from ucall_server_init().
requestinThe received request inside the ucall_received_http_request_callback_t.

Notes

Thread-safe across requests. Two threads must not operate on the same request concurrently.

ucall_server_request_partExtracts one part of a multipart/form-data request body by index.
server.h:1081 ↗
void ucall_server_request_part(ucall_server_request_part_t *arguments);

The payload is handed over without decoding. Media formats are the application's concern - pass data straight to an image or video decoder to keep the path copy-free.

Parameters

serverucall_server_tThe server this call operates on.
outcomeucall_outcome_t *The verb's status, listed on the verb.
requestucall_received_http_request_tThe received request inside the ucall_received_http_request_callback_t.
indexsize_tZero-based index of the part, below ucall_server_request_parts_count().
nameucall_bytes_view_t *Output for the part name from Content-Disposition.
content_typeucall_bytes_view_t *Output for the part's Content-Type.
dataucall_bytes_view_t *Output for the part payload.

Notes

Thread-safe across requests, though two threads must not use one request at once.

Statuses

ucall_server_reply_contentSends an HTTP response with custom headers and body.
server.h:1125 ↗
void ucall_server_reply_content(
    ucall_server_reply_content_t *arguments);

Automatically sets standard headers: Content-Length, Date, Server, Connection.

Parameters

serverucall_server_tThe server this call operates on.
outcomeucall_outcome_t *Output for the outcome, final once its status leaves ucall_status_deferred_k.
requestucall_received_http_request_tThe received request to reply to.
http_statusucall_http_status_tResponse status.
headersucall_bytes_view_t const *Every HTTP header line, each as a counted byte view.
headers_countsize_tNumber of counted header lines.
bodyucall_bytes_view_tResponse body content.

Notes

Thread-safe across requests, though two threads must not use one request at once.

Statuses

ucall_server_reply_errorSends an error response with plain text message.
server.h:1163 ↗
void ucall_server_reply_error(ucall_server_reply_error_t *arguments);

Automatically sets Content-Type: text/plain. For errors requiring custom headers, use ucall_server_reply_content() instead.

Parameters

serverucall_server_tThe server this call operates on.
outcomeucall_outcome_t *Output for the outcome, final once its status leaves ucall_status_deferred_k.
requestucall_received_http_request_tThe received request to reply to.
http_statusucall_http_status_tHTTP error status code (400, 404, 500, etc.).
messageucall_bytes_view_tError message text.

Notes

Thread-safe across requests, though two threads must not use one request at once.

Statuses

ucall_server_reply_vectoredSends a response assembled from several non-contiguous buffers and file ranges.
server.h:1298 ↗
void ucall_server_reply_vectored(
    ucall_server_reply_vectored_t *arguments);

Lets a handler reply with a header, a tensor, and a file without concatenating them into a staging buffer first. Sets Content-Length to the sum of the parts, and sends no body to a HEAD request.

Memory-only replies are final on return. With any file or path part, the outcome reads ucall_status_deferred_k on return: the event loop opens and sizes every part before it sends the head, so Content-Length is measured, then publishes success once every part is sent, or the cause of a failure. A part failing before the head replaces the whole reply: 404 for a missing, unreadable or irregular file, 416 for a range past its end, 500 otherwise. A part failing after the head resets the connection.

ucall_status_invalid_argument_k answers a NULL request, an invalid part, view or range end, a descriptor that reads buffered, a path leaving its directories, a status outside 200 to 599, a 204 or 304 with a body, and a header line that is malformed or sets a length, a transfer encoding, a date or a server name.

Parameters

serverucall_server_tThe server this call operates on.
outcomeucall_outcome_t *Output for the outcome, final once its status leaves ucall_status_deferred_k.
requestucall_received_http_request_tThe received request to reply to.
http_statusucall_http_status_tResponse status.
headersucall_bytes_view_t const *Every HTTP header line, each as a counted byte view.
headers_countsize_tNumber of counted header lines.
partsucall_reply_part_t const *Parts concatenated, in order, into the body.
parts_countsize_tNumber of entries in parts.

Notes

Thread-safe across requests, though two threads must not use one request at once.

Statuses

ucall_server_reply_beginBegins a streamed response, emitting the status line and headers.
server.h:1346 ↗
void ucall_server_reply_begin(ucall_server_reply_begin_t *arguments);

Must be followed by zero or more ucall_server_reply_append() calls and exactly one ucall_server_reply_end() once this call reports success or ucall_status_connection_closed_k; any other failure leaves the request unanswered. The body is chunked for HTTP/1.1 peers. Supplying Content-Length switches to identity encoding, which is what Range responses need, and ucall_server_reply_end() checks the chunks summed to it. An HTTP/1.0 peer without it gets a body delimited by closing the connection. A HEAD request, 204 and 304 carry no body: chunks for a HEAD request are accepted and dropped.

Parameters

serverucall_server_tThe server this call operates on.
outcomeucall_outcome_t *Output for the outcome, final once its status leaves ucall_status_deferred_k.
requestucall_received_http_request_tThe received request to reply to.
http_statusucall_http_status_tResponse status.
headersucall_bytes_view_t const *Every HTTP header line, each as a counted byte view.
headers_countsize_tNumber of counted header lines.

Notes

Thread-safe across requests, though two threads must not use one request at once.

Statuses

ucall_server_reply_appendAppends parts to a response opened with ucall_server_reply_begin().
server.h:1393 ↗
void ucall_server_reply_append(ucall_server_reply_append_t *arguments);

Can be called from any thread, long after the request callback returned. Empty bytes parts send nothing, and so do file ranges of zero bytes.

A file or path part that fails once the head left resets the connection.

ucall_status_invalid_argument_k answers a NULL request, a reply not begun or already ended, an invalid part, view or range end, a descriptor that reads buffered, a path leaving its directories, a zero-length file or path part on a stream with a declared length, as its size is unknown, a 204 or 304 that gets a body, and one call bringing more file and path parts than may wait.

Parameters

serverucall_server_tThe server this call operates on.
outcomeucall_outcome_t *Output for the outcome, final once its status leaves ucall_status_deferred_k.
requestucall_received_http_request_tThe received request being replied to.
partsucall_reply_part_t const *Parts appended, in order, to the body: queued together or not at all.
parts_countsize_tNumber of entries in parts.

Notes

Thread-safe across requests, though two threads must not use one request at once.

Statuses

ucall_server_reply_endCompletes a response opened with ucall_server_reply_begin().
server.h:1432 ↗
void ucall_server_reply_end(ucall_server_reply_end_t *arguments);

Consumes the request handle on every outcome. A response whose chunks fell short of a declared Content-Length closes the connection after the bytes already sent, so the peer cannot mistake the truncated body for a whole one.

While file or path parts are still unsent, the outcome reads ucall_status_deferred_k on return, and the event loop publishes success once they are sent, or the cause of a failure.

Parameters

serverucall_server_tThe server this call operates on.
outcomeucall_outcome_t *Output for the outcome, final once its status leaves ucall_status_deferred_k.
requestucall_received_http_request_tThe received request being replied to.

Notes

Thread-safe across requests, though two threads must not use one request at once.

Statuses