Client#

The client runs one request at a time over a kept-alive connection. See the guide for the event model.

Types#

type lwhttp_client_t#

Opaque HTTP client.

A client runs one request at a time and keeps the connection open between requests when possible. Several clients can run concurrently. Create with lwhttp_client_create(), free with lwhttp_client_destroy().

struct lwhttp_client_config_t#

Client configuration, copied by lwhttp_client_create().

Fill it with lwhttp_client_config_default() first, then change what you need.

uint32_t timeout_ms#

lwIP-CE socket inactivity timeout in milliseconds, covering connect, TLS handshake and transfer. It is extended by any traffic, so it bounds silence, not total request time. 0 disables it. Default 30000.

size_t header_size#

Size in bytes of the buffer that holds the response header section, the line being parsed and any trailers. Each stored field takes name + value + 2 bytes and must leave 128 bytes free for chunk-size and trailer lines; responses that do not fit fail with LWHTTP_ERR_HEADERS_TOO_LARGE. Values below 256 are raised to 256. Default 4096.

uint8_t max_redirects#

Maximum number of redirects followed per request; 0 delivers 3xx responses unchanged. Default 5.

bool keep_alive#

Reuse connections between requests to the same scheme, host and port. When false the client sends Connection: close and opens a new connection for every request. Default true.

const char *user_agent#

User-Agent sent when the request has none, or NULL to send none. The pointer is stored, not copied, so the string must outlive the client. Default "LWHTTP/" LWHTTP_VERSION_STRING.

struct lwhttp_request_t#

A request for lwhttp_client_request().

Zero-initialise it and set at least lwhttp_request_t.url. The struct itself and the URL string are copied; every other pointer (method, target, headers and their strings, body, body_arg) is used until on_complete and must stay valid until then.

The client adds Host, User-Agent (unless supplied here or disabled), Content-Length or Transfer-Encoding: chunked, Accept-Encoding: identity (unless supplied here), Expect: 100-continue and Connection: close as needed. Content-Length: 0 is sent for POST, PUT and PATCH without a body.

const char *method#

Method token, case-sensitive; NULL means "GET".

const char *url#

Absolute http:// or https:// URL. Userinfo and fragment are ignored; localhost, 127.x.x.x and ::1 use lwIP-CE’s loopback interface.

const char *target#

Request-target sent instead of the URL’s path and query (for example "*" or an absolute URL for a proxy), or NULL. Must not contain spaces or control characters. Only the first request uses it; redirect hops send the path and query of the redirect URL. For CONNECT without a target, host:port from the URL is sent.

const lwhttp_header_t *headers#

Extra header fields, sent after the client’s own. A Host, User-Agent or Accept-Encoding field here replaces the client’s. A supplied Host is dropped, and the client’s own sent instead, once a redirect leaves the original scheme, host and port. Content-Length and Transfer-Encoding are rejected with LWHTTP_ERR_ARG; the client sets them from the body fields. Supplying Accept-Encoding disables the content-coding check, so compressed bodies are delivered undecoded.

size_t header_count#

Number of entries in lwhttp_request_t.headers.

const uint8_t *body#

In-memory body of lwhttp_request_t.body_length bytes, or NULL. Ignored when lwhttp_request_t.body_reader is set. Kept for redirects that preserve the body and for automatic retries.

uint32_t body_length#

Body length in bytes, sent as Content-Length. With lwhttp_request_t.body_reader, LWHTTP_LENGTH_UNKNOWN selects a chunked upload. Ignored when neither body nor body_reader is set.

lwhttp_body_reader_t body_reader#

Streams the body instead of lwhttp_request_t.body, or NULL. A streamed body cannot be replayed, so such requests are never retried, and a 307/308 redirect (or a 301/302 for a method other than POST) is delivered to the application instead of followed.

void *body_arg#

Passed to lwhttp_request_t.body_reader.

bool expect_continue#

Send Expect: 100-continue and hold the body until the server answers 100 Continue. If the server stays silent the body is sent on the next lwIP-CE poll tick; if a final response arrives first the body is not sent and the connection is not reused. Ignored when the body is empty.

typedef unsigned long (*lwhttp_body_reader_t)(void *arg, uint8_t *buf, size_t size)#

Supplies streamed request body data.

Called from lwhttp_client_request() or from socket event processing whenever the client can send more. The reader must produce data immediately; it cannot signal “not ready”.

With a known lwhttp_request_t.body_length the reader is asked for at most the bytes still owed and must return between 1 and size; returning 0 early or more than size aborts the request with LWHTTP_ERR_ABORTED. With LWHTTP_LENGTH_UNKNOWN each return value becomes one chunk, and returning 0 ends the body.

Param arg:

lwhttp_request_t.body_arg.

Param buf:

Where to write the data.

Param size:

Maximum number of bytes to write (at most 512).

Return:

Number of bytes written to buf.

struct lwhttp_client_handlers_t#

Application callbacks for one request.

The table is used by pointer and must stay valid until on_complete. Every member may be NULL, and the table pointer itself may be NULL. Callbacks run from inside lwhttp_service_events() (or lwhttp_client_cancel()), never concurrently. Inside any of them the application may call lwhttp_client_cancel() or lwhttp_client_destroy(); destruction is deferred until the callback returns and no further callbacks are made.

bool (*on_response)(lwhttp_client_t *client, void *arg)#

Called once per request when the final response head has arrived: after redirects, not for interim 1xx responses (a 101 is final). Status, version and headers are available through the client accessors.

Param client:

The client.

Param arg:

The arg given to lwhttp_client_request().

Return:

true to continue, false to abort with LWHTTP_ERR_ABORTED.

bool (*on_body)(lwhttp_client_t *client, const uint8_t *data, size_t len, void *arg)#

Called with response body data as it arrives, after on_response. Bodies of followed redirects are discarded and not reported.

Param client:

The client.

Param data:

Body bytes; valid only during the call.

Param len:

Number of bytes in data.

Param arg:

The arg given to lwhttp_client_request().

Return:

true to continue, false to abort with LWHTTP_ERR_ABORTED.

void (*on_complete)(lwhttp_client_t *client, lwhttp_error_t result, void *arg)#

Called exactly once for every request that lwhttp_client_request() accepted, unless the client is destroyed first. It may start a new request on the same client or destroy it.

Param client:

The client.

Param result:

LWHTTP_OK when a complete response was received (any status code), or the error that ended the request.

Param arg:

The arg given to lwhttp_client_request().

struct lwhttp_header_t#

One request header field, used in lwhttp_request_t.headers.

const char *name#

Field name; must be a valid token (see lwhttp_is_token()). Must not be NULL.

const char *value#

Field value; must not be NULL and must not contain control characters other than horizontal tab (so no CR or LF). Bytes 0x80 and above are allowed.

struct lwhttp_client_stats_t#

Counters kept by a client, read with lwhttp_client_stats().

All counters accumulate over the client’s lifetime except lwhttp_client_stats_t.unsent and lwhttp_client_stats_t.final_state, which describe the most recently finished request.

uint16_t connections#

Connections opened (TCP or TLS), including those for redirect hops and retries.

uint16_t reused#

Requests, including redirect hops, started on an already open kept-alive connection.

uint16_t retries#

Automatic retries. A request is retried once on a new connection when a reused connection fails before any response byte arrives, if its method is idempotent (GET, HEAD, PUT, DELETE, OPTIONS, TRACE) and it has no body reader.

uint16_t redirects#

Redirects followed.

uint16_t write_stalls#

Times lwIP-CE refused even a small write for lack of memory; sending resumes on the next event.

uint32_t bytes_sent#

Bytes handed to lwIP-CE (request heads and bodies, before TLS).

uint32_t bytes_received#

Bytes read from lwIP-CE (response heads and bodies, after TLS).

uint32_t unsent#

Nonzero when the last request finished before its whole request was written: request-head and body bytes not yet sent, plus 1 if a chunked upload had not sent its last chunk.

uint8_t final_state#

Client state when the last request finished: 1 while connecting, 2 while exchanging data (0 if no request has finished yet).

LWHTTP_LENGTH_UNKNOWN#

Body length meaning “not known in advance”.

Set lwhttp_request_t.body_length to this value together with lwhttp_request_t.body_reader to upload the body with Transfer-Encoding: chunked. The parser likewise rejects a response Content-Length of exactly 4294967295 with LWHTTP_ERR_TOO_LARGE.

Lifetime#

void lwhttp_client_config_default(lwhttp_client_config_t *config)#

Fill a configuration with the defaults: 30000 ms timeout, 4096-byte header buffer, 5 redirects, keep-alive on, User-Agent: LWHTTP/<version>.

Parameters:
  • config – Configuration to fill.

lwhttp_client_t *lwhttp_client_create(const lwhttp_client_config_t *config)#

Create a client.

Allocates one block from the lwIP-CE heap of about header_size + 1 KB + the client struct. Each request also makes small allocations (URL copy, host name, request head) while it runs. No connection is opened until the first request.

Parameters:
  • config – Configuration to copy, or NULL for the defaults.

Returns:

The new client, or NULL if memory is exhausted.

void lwhttp_client_destroy(lwhttp_client_t *client)#

Destroy a client, closing its connection.

Safe to call at any time. A request in progress is dropped without calling on_complete. Called from inside one of the client’s callbacks, the destruction is deferred until the callback returns and no further callbacks are made. The socket itself is freed by a later lwhttp_service_events().

Parameters:
  • client – The client, or NULL (no effect).

Requests#

lwhttp_error_t lwhttp_client_request(lwhttp_client_t *client, const lwhttp_request_t *request, const lwhttp_client_handlers_t *handlers, void *arg)#

Start a request. Returns immediately; the request progresses inside lwhttp_service_events().

Requests that cannot start fail synchronously and never call on_complete. An accepted request calls on_complete exactly once (unless the client is destroyed first). The client reuses its kept-alive connection when the scheme, host and port match, otherwise it connects; https uses lwIP-CE TLS 1.3 with SNI and certificate verification against lwIP-CE’s trust store.

Redirects 301, 302, 303, 307 and 308 with a Location are followed up to lwhttp_client_config_t.max_redirects. 303 becomes GET (unless the method is HEAD), as do 301 and 302 for POST; the others keep the method and body. Authorization, Proxy-Authorization, Cookie and a supplied Host are no longer sent once a redirect leaves the original scheme, host and port.

Responses with a body that uses a non-identity Content-Encoding (unless the request supplied Accept-Encoding) or a transfer coding other than chunked fail with LWHTTP_ERR_UNSUPPORTED before on_response. A 101 response or a 2xx answer to CONNECT completes the request successfully and keeps the socket for lwhttp_client_detach().

Parameters:
  • client – The client; must not have a request in progress. May be called from on_complete.

  • request – The request; copied, but see lwhttp_request_t for what must stay valid.

  • handlers – Callbacks, or NULL; must stay valid until on_complete.

  • arg – Passed to every handler.

Returns:

LWHTTP_OK if the request was accepted. Otherwise, without calling on_complete: LWHTTP_ERR_ARG (NULL client, request or URL; invalid method, target, header or User-Agent; user Content-Length/Transfer-Encoding), LWHTTP_ERR_STATE (a request is in progress or the client is being destroyed), LWHTTP_ERR_URL (unparsable URL, or no scheme or host), LWHTTP_ERR_UNSUPPORTED (scheme not http/https), LWHTTP_ERR_MEM, or LWHTTP_ERR_CONNECT (lwIP-CE refused to start connecting). Failures detected after the URL was copied (URL, scheme, header and connection errors) also mark the client done with that result for lwhttp_client_result().

lwhttp_error_t lwhttp_client_wait(lwhttp_client_t *client)#

Block until the current request finishes, calling lwhttp_service_events() in a loop. Handlers still run as usual. Must not be called from a client callback.

Parameters:
  • client – The client.

Returns:

The request’s result (see lwhttp_client_result()); immediately the last result if no request is in progress; LWHTTP_ERR_ARG if client is NULL.

lwhttp_error_t lwhttp_client_request_sync(lwhttp_client_t *client, const lwhttp_request_t *request, const lwhttp_client_handlers_t *handlers, void *arg)#

Start a request and wait for it: lwhttp_client_request() followed by lwhttp_client_wait(). Must not be called from a client callback.

Parameters:
  • client – The client.

  • request – The request.

  • handlers – Callbacks, or NULL when the status and headers are enough.

  • arg – Passed to every handler.

Returns:

The synchronous error from lwhttp_client_request(), or the request’s final result.

void lwhttp_client_cancel(lwhttp_client_t *client)#

Abort the request in progress and close its connection.

on_complete is called with LWHTTP_ERR_ABORTED before this function returns. Safe to call from callbacks. No effect if no request is in progress.

Parameters:
  • client – The client, or NULL.

void lwhttp_client_close(lwhttp_client_t *client)#

Close the idle kept-alive connection (or an upgraded connection that was not detached).

No effect while a request is in progress. The next request opens a new connection.

Parameters:
  • client – The client, or NULL.

Results#

bool lwhttp_client_is_done(const lwhttp_client_t *client)#

Check whether the most recent request has finished, successfully or not.

Parameters:
  • client – The client.

Returns:

true once a request has finished and until the next one starts; false before the first request, while one is in progress, or for NULL.

lwhttp_error_t lwhttp_client_result(const lwhttp_client_t *client)#

Result of the most recent request.

Parameters:
  • client – The client.

Returns:

The value passed to on_complete (or the synchronous failure after the URL was accepted); LWHTTP_OK before the first request or while one is in progress; LWHTTP_ERR_ARG for NULL.

uint16_t lwhttp_client_status(const lwhttp_client_t *client)#

Status code of the current or most recent response (the final one after redirects).

Parameters:
  • client – The client.

Returns:

Status code, or 0 if no status line has been received for this request or client is NULL.

uint8_t lwhttp_client_version(const lwhttp_client_t *client)#

Minor HTTP version of the current or most recent response.

Parameters:
  • client – The client.

Returns:

1 for HTTP/1.1, 0 for HTTP/1.0 or if no response was received or client is NULL.

const char *lwhttp_client_header(const lwhttp_client_t *client, const char *name)#

Look up a response header, ignoring case.

The store holds the fields of the final response and, after a chunked body, its trailers. Values remain valid until the next request starts or the client is destroyed.

Parameters:
  • client – The client, may be NULL.

  • name – Field name.

Returns:

Value of the first matching field, or NULL.

const lwhttp_headers_t *lwhttp_client_headers(const lwhttp_client_t *client)#

Access the response header store, for example to iterate with lwhttp_headers_at().

Same contents and lifetime as for lwhttp_client_header(). Do not modify it.

Parameters:
  • client – The client.

Returns:

The store, or NULL if client is NULL.

const char *lwhttp_client_url(const lwhttp_client_t *client)#

URL of the current or most recent request; after redirects, the final URL.

Parameters:
  • client – The client.

Returns:

Client-owned copy, valid until the next request or redirect; NULL before the first request or for NULL.

const lwhttp_client_stats_t *lwhttp_client_stats(const lwhttp_client_t *client)#

Access the client’s counters.

Parameters:
  • client – The client.

Returns:

Pointer into the client, updated live; NULL for NULL.

Upgrades#

struct lwip_socket *lwhttp_client_detach(lwhttp_client_t *client, const uint8_t **pending, size_t *pending_len)#

Take over the socket of an upgraded connection (101 or a 2xx answer to CONNECT).

Call after the request completed with LWHTTP_OK, for example from on_complete. The client unregisters its event handler and forgets the socket; the caller owns it from then on and must register its own handler and eventually lwip_socket_close() and lwip_socket_destroy() it.

Parameters:
  • client – The client.

  • pending – Receives a pointer to bytes the client already read past the response head (the start of the new protocol), or NULL if not wanted. The data lives in the client and is valid until its next request or destruction.

  • pending_len – Receives the number of pending bytes (at most 512), or NULL if not wanted.

Returns:

The socket, or NULL if the last request did not upgrade, the socket was already detached or closed, or client is NULL.