idfxx 1.0.0
Modern C++23 components for ESP-IDF
Loading...
Searching...
No Matches
client.hpp
Go to the documentation of this file.
1// SPDX-License-Identifier: Apache-2.0
2// Copyright 2026 Chris Leishman
3
4#pragma once
5
21#include <idfxx/error>
22#include <idfxx/http/types>
23
24#include <chrono>
25#include <cstdint>
26#include <esp_idf_version.h>
27#include <functional>
28#include <optional>
29#include <span>
30#include <string>
31#include <string_view>
32#include <system_error>
33
34typedef struct esp_http_client* esp_http_client_handle_t;
35struct esp_http_client_event;
36typedef int esp_err_t;
37
38namespace idfxx::http {
39
44enum class event_id : int {
45 // clang-format off
46 error = 0,
47 on_connected = 1,
48 headers_sent = 2,
49 on_header = 3,
50#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(6, 0, 0)
52 on_status_code = 5,
53 on_data = 6,
54 on_finish = 7,
55 disconnected = 8,
56 redirect = 9,
57#else
58 on_data = 4,
59 on_finish = 5,
60 disconnected = 6,
61 redirect = 7,
62#endif
63 // clang-format on
64};
65
70struct event_data {
71 enum event_id id;
72 std::span<const uint8_t> data;
73 std::string_view header_key;
74 std::string_view header_value;
75};
76
87class client {
88public:
92 enum class errc : esp_err_t {
93 // clang-format off
94 max_redirect = 0x7001,
95 connect = 0x7002,
96 write_data = 0x7003,
97 fetch_header = 0x7004,
98 invalid_transport = 0x7005,
99 connecting = 0x7006,
100 eagain = 0x7007,
101 connection_closed = 0x7008,
102 // clang-format on
103 };
104
108 class error_category : public std::error_category {
109 public:
112
114 [[nodiscard]] std::string message(int ec) const override final;
115 };
116
121 // Connection
122 std::string url = {};
123 std::string host = {};
124 int port = 0;
125 std::string path = {};
126 std::string query = {};
128 enum transport transport_type = transport::unknown;
129 std::chrono::milliseconds timeout{5000};
130
131 // Authentication
132 std::string username = {};
133 std::string password = {};
135
136 // TLS (client stores owned copies to keep data alive for the handle)
137 std::string cert_pem = {};
138 std::string client_cert_pem = {};
139 std::string client_key_pem = {};
140
141 // Headers
142 std::string user_agent = {};
143
144 // Redirect
145 bool disable_auto_redirect = false;
146 int max_redirection_count = 0;
147 int max_authorization_retries = 0;
148
149 // Buffers
150 int buffer_size = 0;
151 int buffer_size_tx = 0;
152
153 // Keep-alive
154 bool keep_alive_enable = false;
155 std::chrono::seconds keep_alive_idle{5};
156 std::chrono::seconds keep_alive_interval{5};
157 int keep_alive_count = 3;
158
159 // TLS options
160 bool use_global_ca_store = false;
161 bool skip_cert_common_name_check = false;
162 std::string common_name = {};
174 std::move_only_function<int(void*) const> crt_bundle_attach = {};
175
176 // Event handling
177 std::move_only_function<void(const event_data&) const> on_event = {};
178 };
179
180#ifdef CONFIG_COMPILER_CXX_EXCEPTIONS
188 [[nodiscard]] explicit client(config cfg);
189#endif
190
198
203
204 client(const client&) = delete;
205 client& operator=(const client&) = delete;
206
209
212
213 // =========================================================================
214 // Request Configuration
215 // =========================================================================
216
217#ifdef CONFIG_COMPILER_CXX_EXCEPTIONS
225 void set_url(std::string_view url) { unwrap(try_set_url(url)); }
226
227#endif
228
235 [[nodiscard]] result<void> try_set_url(std::string_view url);
236
242 void set_method(enum method m);
243
252 void set_header(std::string_view key, std::string_view value);
253
259 void delete_header(std::string_view key);
260
270 void set_post_field(std::string_view data);
271
277 void set_username(std::string_view username);
278
284 void set_password(std::string_view password);
285
291 void set_auth_type(enum auth_type type);
292
300 template<typename Rep, typename Period>
301 void set_timeout(const std::chrono::duration<Rep, Period>& timeout) {
302 _set_timeout(std::chrono::ceil<std::chrono::milliseconds>(timeout));
303 }
304
305 // =========================================================================
306 // Simple Request (blocking)
307 // =========================================================================
308
309#ifdef CONFIG_COMPILER_CXX_EXCEPTIONS
326#endif
327
337
338 // =========================================================================
339 // Streaming API
340 // =========================================================================
341
342#ifdef CONFIG_COMPILER_CXX_EXCEPTIONS
354 void open(size_t write_len = 0) { unwrap(try_open(write_len)); }
355
357 void open(int write_len) { open(static_cast<size_t>(write_len)); }
358
367 [[nodiscard]] size_t write(std::span<const uint8_t> data) { return unwrap(try_write(data)); }
368
377 [[nodiscard]] size_t write(std::string_view data) { return unwrap(try_write(data)); }
378
390
399 [[nodiscard]] size_t read(std::span<uint8_t> buf) { return unwrap(try_read(buf)); }
400
409 [[nodiscard]] size_t read(std::span<char> buf) { return unwrap(try_read(buf)); }
410
418
425 void close() { unwrap(try_close()); }
426#endif
427
440
442 [[nodiscard]] result<void> try_open(int write_len) { return try_open(static_cast<size_t>(write_len)); }
443
450 [[nodiscard]] result<size_t> try_write(std::span<const uint8_t> data);
451
458 [[nodiscard]] result<size_t> try_write(std::string_view data);
459
468 // [[nodiscard]] intentionally omitted: callers may fetch headers for their
469 // side effects without needing the returned content-length value.
471
478 [[nodiscard]] result<size_t> try_read(std::span<uint8_t> buf);
479
486 [[nodiscard]] result<size_t> try_read(std::span<char> buf);
487
494
501
502 // =========================================================================
503 // Response Accessors
504 // =========================================================================
505
510 [[nodiscard]] int status_code() const;
511
517
518#ifdef CONFIG_COMPILER_CXX_EXCEPTIONS
534 [[nodiscard]] int chunk_length() const { return unwrap(try_chunk_length()); }
535#endif
536
547
553
560 [[nodiscard]] std::optional<std::string> get_header(std::string_view key) const;
561
569 [[nodiscard]] std::string get_url() const;
570
571 // =========================================================================
572 // Redirect Control
573 // =========================================================================
574
575#ifdef CONFIG_COMPILER_CXX_EXCEPTIONS
587#endif
588
599
606
607 // =========================================================================
608 // Request Control
609 // =========================================================================
610
611#ifdef CONFIG_COMPILER_CXX_EXCEPTIONS
622#endif
623
633
634#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(6, 0, 0)
642 enum class state : int {
643 // clang-format off
644 uninit = 0,
645 init = 1,
646 connecting = 2,
647 connected = 3,
653 close = 9,
654 // clang-format on
655 };
656
664 [[nodiscard]] enum state get_state() const;
665#endif
666
672
673private:
674 client(
676 std::move_only_function<void(const event_data&) const> on_event,
677 std::move_only_function<int(void*) const> crt_bundle_attach,
678 std::string post_data,
679 std::string cert_pem,
680 std::string client_cert_pem,
681 std::string client_key_pem
682 );
683
684 void _set_timeout(std::chrono::milliseconds timeout);
685
686 static result<esp_http_client_handle_t> _init_client(
687 config& cfg,
688 const std::string& cert_pem,
689 const std::string& client_cert_pem,
690 const std::string& client_key_pem
691 );
692 static esp_err_t _http_event_handler(esp_http_client_event* evt);
693 static esp_err_t _crt_bundle_trampoline(void* conf);
694
696 std::move_only_function<void(const event_data&) const> _on_event;
697 std::move_only_function<int(void*) const> _crt_bundle_attach;
698 std::string _post_data;
699 std::string _cert_pem;
700 std::string _client_cert_pem;
701 std::string _client_key_pem;
702};
703
711
717 return {std::to_underlying(e), http_client_category()};
718}
719
731[[nodiscard]] std::unexpected<std::error_code> http_client_error(esp_err_t e);
732
733} // namespace idfxx::http
734
735namespace idfxx {
736
745
746#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(6, 0, 0)
755#endif
756
757} // namespace idfxx
758
760namespace std {
761template<>
762struct is_error_code_enum<idfxx::http::client::errc> : true_type {};
763} // namespace std
766#include "sdkconfig.h"
767#ifdef CONFIG_IDFXX_STD_FORMAT
769#include <algorithm>
770#include <format>
771namespace std {
772
773template<>
774struct formatter<idfxx::http::event_id> {
775 constexpr auto parse(format_parse_context& ctx) { return ctx.begin(); }
776
777 template<typename FormatContext>
778 auto format(idfxx::http::event_id id, FormatContext& ctx) const {
779 auto s = idfxx::to_string(id);
780 return std::copy(s.begin(), s.end(), ctx.out());
781 }
782};
783
784#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(6, 0, 0)
785template<>
786struct formatter<idfxx::http::client::state> {
787 constexpr auto parse(format_parse_context& ctx) { return ctx.begin(); }
788
789 template<typename FormatContext>
790 auto format(idfxx::http::client::state s, FormatContext& ctx) const {
791 auto str = idfxx::to_string(s);
792 return std::copy(str.begin(), str.end(), ctx.out());
793 }
794};
795#endif
796
797} // namespace std
799#endif // CONFIG_IDFXX_STD_FORMAT
800
// end of idfxx_http_client
Error category for HTTP client errors.
Definition client.hpp:108
std::string message(int ec) const override final
Returns a human-readable message for the given error code.
const char * name() const noexcept override final
Returns the name of the error category.
HTTP client with blocking and streaming request support.
Definition client.hpp:87
void delete_header(std::string_view key)
Deletes a request header.
result< void > try_set_url(std::string_view url)
Sets the request URL.
client(client &&) noexcept
Move constructor.
result< size_t > try_read(std::span< char > buf)
Reads response body data into a character buffer.
result< int > try_chunk_length() const
Returns the length of the current chunk in a chunked response.
void perform()
Performs a blocking HTTP request.
Definition client.hpp:325
void set_url(std::string_view url)
Sets the request URL.
Definition client.hpp:225
result< size_t > try_write(std::span< const uint8_t > data)
Writes data to the open connection.
void open(size_t write_len=0)
Opens a connection for streaming.
Definition client.hpp:354
void cancel_request()
Cancels an ongoing HTTP request.
Definition client.hpp:621
int64_t content_length() const
Returns the response content length.
client(config cfg)
Creates an HTTP client from configuration.
void set_password(std::string_view password)
Sets the password for authentication.
result< void > try_cancel_request()
Cancels an ongoing HTTP request.
void set_auth_type(enum auth_type type)
Sets the authentication type.
void set_username(std::string_view username)
Sets the username for authentication.
size_t write(std::span< const uint8_t > data)
Writes data to the open connection.
Definition client.hpp:367
void close()
Closes the connection.
Definition client.hpp:425
void reset_redirect_counter()
Resets the redirect counter to zero.
result< void > try_perform()
Performs a blocking HTTP request.
result< void > try_set_redirection()
Applies the current redirect URL.
result< void > try_close()
Closes the connection.
size_t read(std::span< char > buf)
Reads response body data into a character buffer.
Definition client.hpp:409
result< size_t > try_write(std::string_view data)
Writes string data to the open connection.
std::optional< std::string > get_header(std::string_view key) const
Retrieves a response header value.
result< int64_t > try_fetch_headers()
Reads and processes response headers.
enum state get_state() const
Returns the current lifecycle state of the client.
static result< client > make(config cfg)
Creates an HTTP client from configuration.
result< void > try_flush_response()
Discards any remaining response data.
void set_method(enum method m)
Sets the HTTP method.
void open(int write_len)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition client.hpp:357
void set_header(std::string_view key, std::string_view value)
Sets a request header.
client & operator=(const client &)=delete
void set_redirection()
Applies the current redirect URL.
Definition client.hpp:586
client(const client &)=delete
~client()
Destroys the client and releases all resources.
void set_timeout(const std::chrono::duration< Rep, Period > &timeout)
Sets the request timeout.
Definition client.hpp:301
bool is_chunked_response() const
Checks whether the response uses chunked transfer encoding.
int status_code() const
Returns the HTTP response status code.
void flush_response()
Discards any remaining response data.
Definition client.hpp:417
void set_post_field(std::string_view data)
Sets the POST request body.
result< void > try_open(int write_len)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition client.hpp:442
state
HTTP client lifecycle state.
Definition client.hpp:642
@ req_complete_data
Request data sent.
@ connected
Connected to server.
@ uninit
Client not yet initialized.
@ req_complete_header
Request headers sent.
@ res_complete_data
Response data completed.
@ res_on_data_start
Response data started.
@ res_complete_header
Response headers received.
@ init
Client initialized.
result< size_t > try_read(std::span< uint8_t > buf)
Reads response body data.
int64_t fetch_headers()
Reads and processes response headers.
Definition client.hpp:389
errc
Error codes for HTTP client operations.
Definition client.hpp:92
@ max_redirect
Maximum number of redirects exceeded.
@ fetch_header
Error reading response headers.
@ connection_closed
Connection closed by remote.
@ write_data
Error writing request data.
@ connecting
Connection in progress (async mode)
@ eagain
Resource temporarily unavailable, try again.
@ connect
Connection to server failed.
@ invalid_transport
Invalid transport type specified.
int chunk_length() const
Returns the length of the current chunk in a chunked response.
Definition client.hpp:534
result< void > try_open(size_t write_len=0)
Opens a connection for streaming.
size_t read(std::span< uint8_t > buf)
Reads response body data.
Definition client.hpp:399
std::string get_url() const
Retrieves the current effective URL.
size_t write(std::string_view data)
Writes string data to the open connection.
Definition client.hpp:377
esp_http_client_handle_t idf_handle() const noexcept
Returns the underlying ESP-IDF HTTP client handle.
Definition client.hpp:671
Concept for types that can be both received and posted as event data.
Definition event.hpp:164
int esp_err_t
Definition error.hpp:35
std::string to_string(core_id c)
Returns a string representation of a CPU core identifier.
Definition cpu.hpp:52
struct esp_http_client * esp_http_client_handle_t
Definition client.hpp:34
int esp_err_t
Definition client.hpp:36
method
HTTP request methods.
Definition types.hpp:29
auth_type
HTTP authentication types.
Definition types.hpp:56
@ none
No authentication.
event_id
HTTP client event identifiers.
Definition client.hpp:44
@ on_data
Received response body data.
@ on_header
Received a response header.
@ on_headers_complete
All response headers have been received.
@ disconnected
Disconnected from the server.
@ on_status_code
Received the HTTP status code from the server.
@ on_connected
Connected to the server.
@ headers_sent
All request headers have been sent.
@ on_finish
Finished a complete HTTP session.
@ error
An error occurred during the HTTP operation.
@ redirect
Intercepting a redirect.
std::unexpected< std::error_code > http_client_error(esp_err_t e)
Creates an unexpected error from an ESP-IDF error code, mapping to HTTP client error codes where poss...
std::error_code make_error_code(client::errc e) noexcept
Creates an error code from an idfxx::http::client::errc value.
Definition client.hpp:716
const client::error_category & http_client_category() noexcept
Returns a reference to the HTTP client error category singleton.
transport
HTTP transport types.
Definition types.hpp:68
@ unknown
Unknown transport.
T unwrap(result< T > result)
Throws a std::system_error if the result is an error.
Definition error.hpp:307
errc
IDFXX error codes.
Definition error.hpp:46
@ timeout
Operation timed out.
std::expected< T, std::error_code > result
result type wrapping a value or error code.
Definition error.hpp:120
HTTP client configuration.
Definition client.hpp:120
std::string_view header_key
Header name for on_header events.
Definition client.hpp:73
std::string_view header_value
Header value for on_header events.
Definition client.hpp:74
std::span< const uint8_t > data
Payload for on_data events.
Definition client.hpp:72
enum event_id id
Event type.
Definition client.hpp:71