#pragma once #include #include "http_head.h" namespace roro::release { // Carrying on a download that broke (issue #54): the connection is opened again with // "Range: bytes=-", and what was already received stays where it is. The Update File's own // hash and signature are what decide whether the whole is good; these checks only keep the device // from carrying on with something that plainly is not the rest of the same file. // The validator to send as "If-Range", so a file that changed comes back whole (a 200) instead of // in part: only a strong ETag may be used for that. "" for none. inline std::string ifRangeFor(const std::string& etag) { return etag.size() >= 2 && etag.front() == '"' && etag.back() == '"' ? etag : ""; } // Is this answer the rest of the file, from byte `from` of `total`? "" if so, or why not, in // words for the screen. `etag` is what the first answer gave ("" if nothing). inline std::string resumeRefusal(const HttpHead& head, long from, long total, const std::string& etag) { if (head.status == 200) return "The file changed on the server"; if (head.status != 206) return "The server answered " + std::to_string(head.status); if (head.chunked || head.rangeFrom != from) return "The server sent another part of the file"; if (head.rangeTotal != total || head.rangeTo != total - 1 || head.contentLength != total - from) return "The file changed size on the server"; if (!etag.empty() && !head.etag.empty() && head.etag != etag) return "The file changed on the server"; return ""; } // How long to keep trying. A break that came after new bytes is the link being poor, and is // worth another try; breaks with nothing in between mean it is not coming back. class ResumePolicy { public: static constexpr int kMaxResumes = 20; // in one download static constexpr int kMaxStuck = 3; // in a row without a byte // The connection broke with `received` bytes of the file here: try again? bool again(long received) { stuck_ = received > last_ ? 0 : stuck_ + 1; last_ = received; if (resumes_ >= kMaxResumes || stuck_ >= kMaxStuck) return false; resumes_++; return true; } int resumes() const { return resumes_; } private: long last_ = 0; int resumes_ = 0, stuck_ = 0; }; } // namespace roro::release