DeterministicESPAsyncWebServer v6.27.1
Zero-allocation, bounded-execution async HTTP server for ESP32
Loading...
Searching...
No Matches
http_client.h
Go to the documentation of this file.
1// Copyright (C) 2026 Douglas Quigg (dstroy0) <dquigg123@gmail.com>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4/**
5 * @file http_client.h
6 * @brief Zero-heap outbound HTTP(S) client over raw lwIP (DETWS_ENABLE_HTTP_CLIENT).
7 *
8 * A blocking client for issuing requests *from* the device: webhooks, telemetry
9 * push, REST calls. Split, like the other services, into a pure host-testable
10 * core and an ESP32-only transport:
11 *
12 * - http_client_parse_url() / http_client_build_request() /
13 * http_client_parse_response() are pure string functions, unit-tested on the
14 * host (env:native_http_client).
15 * - http_get() / http_post() resolve the host (DNS), open a raw lwIP TCP
16 * connection (https:// via client-side mbedTLS over the shared static arena),
17 * send the request, and fill the result from a fixed BSS receive buffer. No
18 * heap; one request at a time (single-task device).
19 *
20 * Response bodies are delimited by Content-Length or by connection close;
21 * chunked transfer-decoding is applied when present. The body is returned by
22 * pointer into the client's static buffer (valid until the next call).
23 */
24
25#ifndef DETERMINISTICESPASYNCWEBSERVER_HTTP_CLIENT_H
26#define DETERMINISTICESPASYNCWEBSERVER_HTTP_CLIENT_H
27
28#include "ServerConfig.h"
29#include <stddef.h>
30#include <stdint.h>
31
32#if DETWS_ENABLE_HTTP_CLIENT
33
34// Transport / result error codes (negative; HTTP status codes are positive).
35enum class HttpClientError : int32_t
36{
37 HTTP_CLIENT_ERR_URL = -1, ///< Malformed URL.
38 HTTP_CLIENT_ERR_DNS = -2, ///< Host resolution failed.
39 HTTP_CLIENT_ERR_CONNECT = -3, ///< TCP/TLS connect failed.
40 HTTP_CLIENT_ERR_TIMEOUT = -4, ///< No (complete) response before the timeout.
41 HTTP_CLIENT_ERR_SEND = -5, ///< Failed to send the request.
42 HTTP_CLIENT_ERR_RESPONSE = -6, ///< Malformed / unparseable response.
43 HTTP_CLIENT_ERR_TLS = -7, ///< HTTPS requested but TLS unavailable / handshake failed.
44};
45
46/** @brief Result of an HTTP client request. */
47struct HttpClientResult
48{
49 int status; ///< HTTP status code (e.g. 200), or a negative HttpClientError.
50 const uint8_t *body; ///< Response body (points into the client's static buffer).
51 size_t body_len; ///< Response body length in bytes.
52};
53
54// ---------------------------------------------------------------------------
55// Pure helpers (host-testable; no sockets, no heap)
56// ---------------------------------------------------------------------------
57
58/**
59 * @brief Parse an absolute URL into scheme/host/port/path.
60 *
61 * Accepts `http://host[:port][/path]` and `https://...`. Defaults: port 80
62 * (http) / 443 (https), path "/".
63 *
64 * @return true on success; false if malformed or a field overflows its buffer.
65 */
66bool http_client_parse_url(const char *url, bool *is_https, char *host, size_t host_cap, uint16_t *port, char *path,
67 size_t path_cap);
68
69/**
70 * @brief Build an HTTP/1.1 request line + headers (+ optional body) into @p out.
71 *
72 * Emits `Host`, `User-Agent`, `Connection: close`, and (when @p body) a
73 * `Content-Type` + `Content-Length`.
74 *
75 * @return number of bytes written, or 0 if it would not fit @p cap.
76 */
77size_t http_client_build_request(const char *method, const char *host, uint16_t port, const char *path,
78 const char *content_type, const uint8_t *body, size_t body_len, char *out, size_t cap);
79
80/**
81 * @brief Parse a complete HTTP response: status code + body location.
82 *
83 * Locates the body after the header terminator and bounds it by Content-Length,
84 * decodes chunked transfer-encoding in place, or (absent both) treats the rest
85 * as the body (connection-close framing).
86 *
87 * @param buf full response bytes.
88 * @param len number of bytes in @p buf (mutable: chunked decode rewrites in place).
89 * @param body_off receives the body offset within @p buf.
90 * @param body_len receives the (decoded) body length.
91 * @return the HTTP status code, or a negative HttpClientError on a malformed response.
92 */
93int http_client_parse_response(uint8_t *buf, size_t len, size_t *body_off, size_t *body_len);
94
95// ---------------------------------------------------------------------------
96// Transport (ESP32 only; returns HTTP_CLIENT_ERR_* on a host build)
97// ---------------------------------------------------------------------------
98
99/** @brief Blocking GET @p url. @return the status code (>0) or a negative HttpClientError. */
100int http_get(const char *url, HttpClientResult *out);
101
102/**
103 * @brief Blocking POST @p body to @p url with @p content_type.
104 * @return the status code (>0) or a negative HttpClientError.
105 */
106int http_post(const char *url, const char *content_type, const uint8_t *body, size_t body_len, HttpClientResult *out);
107
108// ---------------------------------------------------------------------------
109// https:// server authentication (optional; needs DETWS_ENABLE_HTTP_CLIENT_TLS)
110// ---------------------------------------------------------------------------
111// By default the client encrypts but does NOT authenticate the server (no trust
112// store). Install a CA and/or a certificate pin to authenticate the peer; calls
113// are no-ops on a build without client TLS. Set once before issuing requests.
114
115/** @brief Trust anchor for https:// verification (PEM incl. NUL, or DER; nullptr clears). */
116void http_client_set_ca(const uint8_t *ca, size_t ca_len);
117
118/** @brief Pin the server certificate by its SHA-256 (32 bytes of the DER; nullptr clears). */
119void http_client_set_pin(const uint8_t sha256[32]);
120
121/** @brief Clear any installed CA / pin (back to encrypt-only). */
122void http_client_clear_verify();
123
124#endif // DETWS_ENABLE_HTTP_CLIENT
125
126#endif // DETERMINISTICESPASYNCWEBSERVER_HTTP_CLIENT_H
User-facing configuration for DeterministicESPAsyncWebServer.