Files
roro9stack/src/platform/https_get.h
T
twislaandClaude Opus 5.5 472f13260f
CI / build (pull_request) Successful in 2m6s
Site / build (pull_request) Successful in 11s
Updates: a download that breaks is carried on
When the connection breaks during a release download (issue #54), the
device waits up to a minute for Wi-Fi, asks for the rest of the file
with a Range request and carries on: the slot and the hash so far are
kept. It gives up after three tries in a row that bring nothing.
Nothing is kept across a restart.

Carrying on asks for 64 KB free, not 80: between two connections of an
install 78 KB is free, and the first attempt on the device was refused
for that. update damage cut now breaks every connection after n bytes.

Tried on the device: v0.23.0 installed over four connections, and a
download that brought nothing given up with nothing switched.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0162FokPdvY2KsS4NBfwyWPk
2026-10-11 18:26:39 +02:00

63 lines
2.8 KiB
C++

#pragma once
#include <NetworkClientSecure.h>
#include <memory>
#include <string>
#include "http_head.h"
#include "platform/counted_client.h"
namespace roro {
// One HTTPS GET, read as a stream: the connection checks the server's chain and name against the
// roots in ca_roots.h, the head is parsed, and the body comes out whole whether the server sent it
// with a length or in chunks. Used by the Update Service to read Gitea's answers and to download a
// release. Redirects aren't followed: the device only goes where it was told to (Q163).
class HttpsGet {
public:
// A TLS connection to Gitea peaks at about 52 KB of heap (measured: the same with or without
// checking the certificate); with Q86's 20 KB to spare, it starts with at least this much free.
static constexpr size_t kNeedFree = 80 * 1024;
// Carrying on a download (issue #54) happens in the middle of an install, whose own buffers
// (about 8 KB, measured: 78 KB free between two connections) are already taken out of what is
// free, and with the screen taken over nothing else will ask for memory: the peak and 12 KB.
static constexpr size_t kNeedFreeToCarryOn = 64 * 1024;
explicit HttpsGet(net::User user) : tls_(user) {}
~HttpsGet() { close(); }
// Connects and reads the head. "" when a 200 is on its way, or why not, in words for the screen.
std::string open(const std::string& host, const std::string& path, const char* accept);
// The same, for the rest of a file from byte `from` (issue #54): asks for that range, with
// `ifRange` (an ETag, or "") so that a file that changed comes whole. The answer, a 206 or
// not, is for the caller to judge with head(): "" here only says a head was read.
std::string openFrom(const std::string& host, const std::string& path, const char* accept, long from, const std::string& ifRange);
long contentLength() const { return head_.contentLength; } // -1: not known
const release::HttpHead& head() const { return head_; }
// Body bytes into `buf`: how many, 0 at the end, -1 when the connection broke or stalled
// before the end.
int read(uint8_t* buf, size_t len);
void close();
private:
static constexpr size_t kRaw = 1024;
static constexpr uint32_t kStallMs = 10000;
int readRaw(uint8_t* into, size_t len); // from the connection, waiting up to kStallMs
std::string request(const std::string& host, const std::string& path, const char* accept, const std::string& more, size_t needFree);
Counted<NetworkClientSecure> tls_;
release::HttpHead head_;
release::ChunkedDecoder chunks_;
std::unique_ptr<uint8_t[]> raw_;
std::string early_; // body bytes that arrived with the head
size_t earlyAt_ = 0;
long left_ = -1; // body bytes still to come, if the server said how many
bool done_ = false;
};
} // namespace roro