#pragma once #include #include #include #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 tls_; release::HttpHead head_; release::ChunkedDecoder chunks_; std::unique_ptr 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