Skip to content

Instantly share code, notes, and snippets.

@skrat
Last active April 28, 2026 07:11
Show Gist options
  • Select an option

  • Save skrat/9027c6281f3deacd256f2f885ec1657e to your computer and use it in GitHub Desktop.

Select an option

Save skrat/9027c6281f3deacd256f2f885ec1657e to your computer and use it in GitHub Desktop.
ESP HTTP client C++ std streambuf and istream supports chunked responses
#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