// SPDX-License-Identifier: GPL-3.0-only // Pure, hardware-independent HTTP Range header parser. Header-only so it compiles both on // the ESP32 and on the host for native unit tests (see firmware/test, docs/testing.md). #pragma once #include namespace oshttp { struct RangeResult { bool partial; // a "bytes=" range was requested bool satisfiable; // start is within the file size_t start; // inclusive size_t end; // inclusive }; // Parse a Range header value like "bytes=START-END" (END optional) against a known total // size. If header is null/empty or not a bytes range, returns partial=false (send full // body). A start past the end sets satisfiable=false (caller sends 416). inline RangeResult parseByteRange(const char *header, size_t total) { RangeResult r{false, true, 0, total ? total - 1 : 0}; if (!header) return r; const char *prefix = "bytes="; for (size_t i = 0; prefix[i]; ++i) { if (header[i] != prefix[i]) return r; // not a bytes range -> full body } r.partial = true; const char *s = header + 6; size_t start = 0; while (*s >= '0' && *s <= '9') { start = start * 10 + (*s - '0'); ++s; } if (*s != '-') { r.partial = false; return r; } // malformed -> full body ++s; size_t end = total ? total - 1 : 0; bool anyEnd = false; size_t ev = 0; while (*s >= '0' && *s <= '9') { ev = ev * 10 + (*s - '0'); ++s; anyEnd = true; } if (anyEnd) end = ev; if (start >= total) { r.satisfiable = false; r.start = start; r.end = end; return r; } if (end >= total) end = total ? total - 1 : 0; r.start = start; r.end = end; return r; } } // namespace oshttp