HTTP

Provides high-performance HTTP/1.1 server functionality built on libevpl.

The libevpl HTTP server is very minimalist in its current form, but it allows HTTP to be transmitted over arbitrary supported protocols possibly with zero-copy transfer.

Overview

libevpl’s HTTP support provides:

  • HTTP/1.0 and HTTP/1.1 server - Handle GET, POST, PUT, DELETE, HEAD requests, at whichever version the request claims
  • Streaming - Support for chunked transfer encoding and content-length
  • Zero-copy - Use iovecs for efficient data transfer
  • Header manipulation - Add custom request/response headers
  • Multiple protocols - Run HTTP over TCP, XLIO, or RDMA

Supported methods: GET, HEAD, POST, PUT, DELETE

What the library decides for you

Message framing belongs to the library, not the application: it is the only part that knows both what the peer asked for and what is about to reach the wire. Specifically —

  • Date is added to every response (RFC 9110 §6.6.1 makes it a MUST on 2xx, 3xx and 4xx). Do not add one; two would be a malformed message.
  • Host is added to every client request that does not already carry one (RFC 9112 §3.2 makes it a MUST on HTTP/1.1). Add one only to override the authority the endpoint implies.
  • The framing headers are chosen from the status and the request’s version, not only from what the application asked for. A 1xx or 204 response carries neither Content-Length nor Transfer-Encoding (RFC 9110 §8.6 and RFC 9112 §6.1); a chunked response to an HTTP/1.0 request is sent close-delimited instead, because that version has no chunked coding to decode.
  • The content of a response to HEAD, and of a 1xx, 204 or 304, is suppressed: the header fields a GET would have returned are still sent, and the octets are not (RFC 9112 §6.3).
  • A status that is not a status — outside 100..599 — is answered 500 with the original logged, since it cannot be put on the wire at all.

A conformance suite for all of this lives in src/http/tests; see src/http/tests/CONFORMANCE.md.

Types

struct evpl_http_agent

Opaque structure representing an HTTP agent (per event loop).

struct evpl_http_server

Opaque structure representing an HTTP server attached to a listener.

struct evpl_http_request

Opaque structure representing an individual HTTP request/response.

enum evpl_http_notify_type

HTTP event notifications:

Type Description
EVPL_HTTP_NOTIFY_RECEIVE_DATA Request body data available
EVPL_HTTP_NOTIFY_RECEIVE_COMPLETE Request fully received
EVPL_HTTP_NOTIFY_WANT_DATA Server ready for more response data
EVPL_HTTP_NOTIFY_RESPONSE_COMPLETE Response fully sent
EVPL_HTTP_NOTIFY_RESPONSE_HEADERS Client: status line and response headers received
EVPL_HTTP_NOTIFY_FAILED The request is over and will not complete

Exactly one of RECEIVE_COMPLETE (client), RESPONSE_COMPLETE (server) or FAILED reaches a given request, so FAILED is where an application releases whatever it attached to one. Every request still outstanding on a connection gets it when the connection goes down, so a caller is never left waiting on a completion that can no longer happen. evpl_http_request_status() carries the reason:

Reason Meaning
EVPL_HTTP_ERROR_CONN_LOST The connection was lost before the request completed
EVPL_HTTP_ERROR_BAD_RESPONSE Client: the peer’s response could not be parsed

The request is freed as soon as the callback returns, so nothing may reference it afterwards.

enum evpl_http_request_type

HTTP request methods:

  • EVPL_HTTP_REQUEST_TYPE_GET
  • EVPL_HTTP_REQUEST_TYPE_HEAD
  • EVPL_HTTP_REQUEST_TYPE_POST
  • EVPL_HTTP_REQUEST_TYPE_PUT
  • EVPL_HTTP_REQUEST_TYPE_DELETE
  • EVPL_HTTP_REQUEST_TYPE_UNKNOWN

Callback Types

evpl_http_notify_callback_t

typedef void (*evpl_http_notify_callback_t)(
    struct evpl                *evpl,
    struct evpl_http_agent     *agent,
    struct evpl_http_request   *request,
    enum evpl_http_notify_type  notify_type,
    enum evpl_http_request_type request_type,
    const char                 *uri,
    void                       *notify_data,
    void                       *private_data);

Callback invoked for HTTP events on a specific request.

Parameters:

  • evpl - Event loop
  • agent - HTTP agent
  • request - HTTP request
  • notify_type - Type of notification
  • request_type - HTTP method (GET, POST, etc.)
  • uri - Request URI
  • notify_data - Request-specific data (from dispatch callback)
  • private_data - User context (from dispatch callback)

evpl_http_dispatch_callback_t

typedef void (*evpl_http_dispatch_callback_t)(
    struct evpl                 *evpl,
    struct evpl_http_agent      *agent,
    struct evpl_http_request    *request,
    evpl_http_notify_callback_t *notify_callback,
    void                       **notify_data,
    void                        *private_data);

Callback invoked when a new HTTP request arrives (routing/dispatch).

Parameters:

  • evpl - Event loop
  • agent - HTTP agent
  • request - New HTTP request
  • notify_callback - [OUT] Set notification callback for this request
  • notify_data - [OUT] Set request-specific context
  • private_data - Server context

Functions

Agent Management

evpl_http_init

struct evpl_http_agent *evpl_http_init(struct evpl *evpl);

Create an HTTP agent for an event loop.

Parameters:

  • evpl - Event loop

Returns: HTTP agent, or NULL on failure

Note: One agent per event loop.


evpl_http_destroy

void evpl_http_destroy(struct evpl_http_agent *agent);

Destroy an HTTP agent. All servers must be detached first.

Parameters:

  • agent - Agent to destroy

Server Management

evpl_http_attach

struct evpl_http_server *evpl_http_attach(
    struct evpl_http_agent       *agent,
    struct evpl_listener         *listener,
    evpl_http_dispatch_callback_t dispatch_callback,
    void                         *private_data);

Attach an HTTP server to a listener.

Parameters:

  • agent - HTTP agent
  • listener - Network listener
  • dispatch_callback - Request dispatch callback
  • private_data - Server context

Returns: HTTP server handle, or NULL on failure


evpl_http_server_destroy

void evpl_http_server_destroy(
    struct evpl_http_agent  *agent,
    struct evpl_http_server *server);

Detach and destroy an HTTP server.

Parameters:

  • agent - HTTP agent
  • server - Server to destroy

Request Information

evpl_http_request_type

enum evpl_http_request_type evpl_http_request_type(
    struct evpl_http_request *request);

Get the HTTP method of a request.

Returns: Request type enum


evpl_http_request_type_to_string

const char *evpl_http_request_type_to_string(
    struct evpl_http_request *request);

Get the HTTP method as a string.

Returns: Method string (“GET”, “POST”, etc.)


evpl_http_request_url

const char *evpl_http_request_url(
    struct evpl_http_request *request,
    int                      *len);

Get the request URI.

Parameters:

  • request - HTTP request
  • len - [OUT] URI length (optional, can be NULL)

Returns: URI string


evpl_http_request_header

const char *evpl_http_request_header(
    struct evpl_http_request *request,
    const char               *name);

Get a request header value.

Parameters:

  • request - HTTP request
  • name - Header name (case-insensitive)

Returns: Header value, or NULL if not present


evpl_http_request_header_iterate

typedef void (*evpl_http_request_header_cb_t)(
    const char *name,
    const char *value,
    void       *private_data);

void evpl_http_request_header_iterate(
    struct evpl_http_request     *request,
    evpl_http_request_header_cb_t callback,
    void                         *private_data);

Invoke callback once for every header on the request, in the order the headers were received. Use this when the set of header names isn’t known up front — for example, when canonicalizing headers for signature verification.

The name and value pointers passed to the callback are owned by the request and remain valid for the duration of the call only; copy them if they need to outlive the callback.

Parameters:

  • request - HTTP request
  • callback - Function to invoke for each header
  • private_data - Opaque pointer forwarded to callback

Request Body

evpl_http_request_get_data_avail

uint64_t evpl_http_request_get_data_avail(
    struct evpl_http_request *request);

Get the number of bytes available to read from request body.

Returns: Available bytes


evpl_http_request_get_datav

int evpl_http_request_get_datav(
    struct evpl              *evpl,
    struct evpl_http_request *request,
    struct evpl_iovec        *iov,
    int                       length);

Read request body data into iovecs.

Parameters:

  • evpl - Event loop
  • request - HTTP request
  • iov - [OUT] Iovec to receive data
  • length - Maximum bytes to read

Returns: Number of bytes read


Response Headers

evpl_http_request_add_header

int evpl_http_request_add_header(
    struct evpl_http_request *request,
    const char               *name,
    const char               *value);

Add a header to the outbound block — response headers on a server connection, request headers on a client one.

Parameters:

  • request - HTTP request
  • name - Header name
  • value - Header value

Returns: 0, or -1 with the header not added if either:

  • it would push the block past the configured http_max_header_size, or
  • the name is not a token (RFC 9110 §5.1), or the value contains CR or LF, which §5.5 calls “invalid and dangerous” and puts outside the field-value grammar. A CRLF in a value ends the field, so everything after it would be read as further fields and then as content — one message becoming two, the second chosen by whoever supplied the value. Worth testing the return wherever a value comes from outside the program.

Response Body

evpl_http_server_set_response_length

void evpl_http_server_set_response_length(
    struct evpl_http_request *request,
    uint64_t                  content_length);

Set the response Content-Length.

Parameters:

  • request - HTTP request
  • content_length - Response body size in bytes

Note: Call before sending data.


evpl_http_server_set_response_chunked

void evpl_http_server_set_response_chunked(
    struct evpl_http_request *request);

Enable chunked transfer encoding for the response.

Parameters:

  • request - HTTP request

Use case: When response size is not known in advance.


evpl_http_request_add_datav

void evpl_http_request_add_datav(
    struct evpl_http_request *request,
    struct evpl_iovec        *iov,
    int                       niov);

Add response body data.

Parameters:

  • request - HTTP request
  • iov - Iovecs containing response data
  • niov - Number of iovecs

evpl_http_server_dispatch_default

void evpl_http_server_dispatch_default(
    struct evpl_http_request *request,
    int                       status);

Send a default response with a status code.

Parameters:

  • request - HTTP request
  • status - HTTP status code (200, 404, 500, etc.)

Use case: Quick responses for errors or simple status pages.


Client Connections

evpl_http_client_connect

struct evpl_http_conn *evpl_http_client_connect(
    struct evpl_http_agent *agent,
    enum evpl_protocol_id   protocol_id,
    struct evpl_endpoint   *endpoint,
    enum evpl_http_version  version,
    void                   *private_data);

Open a client connection. The handle belongs to the caller until evpl_http_client_close.


evpl_http_client_close

void evpl_http_client_close(
    struct evpl_http_agent *agent,
    struct evpl_http_conn  *conn);

Release a client connection.

The handle stays valid even after the peer has gone away: a dropped connection is retired but not freed, so the pointer its owner holds never becomes stale at a moment the owner cannot observe. Every request outstanding on it is completed with EVPL_HTTP_NOTIFY_FAILED when that happens, which is how the owner learns.

Calling this on a connection whose peer has already gone is therefore fine, and is how such a connection is finally released. Dispatching a request on one is also safe: it completes immediately with EVPL_HTTP_NOTIFY_FAILED, before evpl_http_request_dispatch returns.

The handle must not be used afterwards.


See Also


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