Last active
April 28, 2026 07:11
-
-
Save skrat/9027c6281f3deacd256f2f885ec1657e to your computer and use it in GitHub Desktop.
ESP HTTP client C++ std streambuf and istream supports chunked responses
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #include "esp_crt_bundle.h" | |
| #include "esp_err.h" | |
| #include "esp_event.h" | |
| #include "esp_http_client.h" | |
| #include "esp_log.h" | |
| #include "esp_netif.h" | |
| #include "qemu_internet.h" | |
| #include <cstddef> | |
| #include <cstring> | |
| #include <iostream> | |
| #include <istream> | |
| #include <ostream> | |
| #include <sstream> | |
| #include <streambuf> | |
| #include <nlohmann/json.hpp> | |
| using json = nlohmann::json; | |
| const static char *TAG = "app"; | |
| class http_client_streambuf : public std::streambuf { | |
| private: | |
| esp_http_client_handle_t &client_; | |
| std::vector<char> buffer_; | |
| /* copy ctor and assignment not implemented; copying not allowed */ | |
| http_client_streambuf(const http_client_streambuf &client); | |
| http_client_streambuf &operator=(const http_client_streambuf &client); | |
| public: | |
| http_client_streambuf(esp_http_client_handle_t &client, | |
| std::size_t buff_size = 1024) | |
| : client_(client), buffer_(buff_size) {} | |
| int_type underflow() { | |
| if (this->gptr() == this->egptr()) { | |
| int n = esp_http_client_read(client_, buffer_.data(), buffer_.size()); | |
| if (n <= 0) { | |
| if (!esp_http_client_is_complete_data_received(client_)) { | |
| ESP_LOGE(TAG, "Error reading response"); | |
| } | |
| return std::char_traits<char>::eof(); | |
| } | |
| this->setg(this->buffer_.data(), this->buffer_.data(), | |
| this->buffer_.data() + n); | |
| } | |
| return this->gptr() == this->egptr() | |
| ? std::char_traits<char>::eof() | |
| : std::char_traits<char>::to_int_type(*this->gptr()); | |
| } | |
| }; | |
| static const std::vector<std::string> URLs = { | |
| // regular | |
| "https://jsonplaceholder.typicode.com/todos/1", | |
| // chunked | |
| "https://api.open-meteo.com/v1/" | |
| "forecast?latitude=35.691405139332&longitude=139.64870184901&" | |
| "hourly=temperature_2m&daily=weather_code", | |
| }; | |
| extern "C" void app_main(void) { | |
| ESP_LOGE(TAG, "Hello world"); | |
| ESP_ERROR_CHECK(esp_netif_init()); | |
| ESP_ERROR_CHECK(esp_event_loop_create_default()); | |
| ESP_ERROR_CHECK(qemu_internet_connect()); | |
| for (const auto &url : URLs) { | |
| esp_http_client_config_t config{}; | |
| config.url = url.c_str(); | |
| config.method = HTTP_METHOD_GET; | |
| config.disable_auto_redirect = true; | |
| config.crt_bundle_attach = esp_crt_bundle_attach; | |
| auto client = esp_http_client_init(&config); | |
| ESP_LOGI(TAG, "Getting %s", config.url); | |
| esp_err_t http_err; | |
| // This has to be called before reading from http_client_streambuf | |
| if ((http_err = esp_http_client_open(client, 0)) != ESP_OK) { | |
| ESP_LOGE(TAG, "Failed to open HTTP connection: %s", | |
| esp_err_to_name(http_err)); | |
| } else { | |
| // This has to be called before reading from http_client_streambuf | |
| auto err = esp_http_client_fetch_headers(client); | |
| if (err < 0) { | |
| ESP_LOGE(TAG, "Failed to read headers: %s", esp_err_to_name(err)); | |
| } else { | |
| http_client_streambuf buf(client, 256); | |
| std::istream input(&buf); | |
| auto j = json::parse(input); | |
| std::cout << j << std::endl; | |
| // std::stringstream ss; | |
| // ss << input.rdbuf(); | |
| // std::cout << ss.str() << std::endl; | |
| } | |
| } | |
| esp_http_client_cleanup(client); | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment