Keep the graceful final-ACK TCP close and optional FEM control guards from pixitha's browser compatibility fixes. Replace Brotli negotiation and duplicate page assets with one gzip representation compressed by Google Zopfli 0.4.3 using 1,000 iterations. Compress the bootstrap, captive success, compact OTA, and static WiFi setup pages with the same validated build cache. Honor gzip quality values and wildcard precedence. Validate compressed OTA page evidence in the firmware capability gate, and add native and Python regressions plus CI coverage. Remove the unused Brotli build dependency. Validation: 54 native tests, 56 Python/browser checks, full V4.3 build and verified application flash. Actual V4 WiFi responses matched the generated gzip assets; 30 repeat downloads completed without reboot. Chromium loaded the setup and settings pages without script or transfer errors. Physical Android testing is pending because the Pi detects no phone over USB or mDNS.
79 lines
2.7 KiB
C++
79 lines
2.7 KiB
C++
#pragma once
|
|
|
|
#include <stddef.h>
|
|
#include <string.h>
|
|
|
|
namespace mesh {
|
|
namespace http {
|
|
|
|
inline bool encodingTokenEquals(const char* first, const char* last, const char* token) {
|
|
while (first != last && *token) {
|
|
char c = *first++;
|
|
if (c >= 'A' && c <= 'Z') c += 'a' - 'A';
|
|
if (c != *token++) return false;
|
|
}
|
|
return first == last && *token == 0;
|
|
}
|
|
|
|
inline void trimEncodingToken(const char*& first, const char*& last) {
|
|
while (first != last && (*first == ' ' || *first == '\t')) ++first;
|
|
while (first != last && (last[-1] == ' ' || last[-1] == '\t')) --last;
|
|
}
|
|
|
|
// A missing header permits any coding (RFC 9110). An empty header requests
|
|
// identity only. An explicit gzip preference takes precedence over '*'.
|
|
inline bool acceptsGzip(const char* header) {
|
|
if (!header) return true;
|
|
bool wildcard = false;
|
|
bool explicit_gzip = false;
|
|
bool gzip_allowed = false;
|
|
const char* item = header;
|
|
while (*item) {
|
|
const char* end = strchr(item, ',');
|
|
if (!end) end = item + strlen(item);
|
|
const char* semi = static_cast<const char*>(memchr(item, ';', end - item));
|
|
const char* first = item;
|
|
const char* last = semi ? semi : end;
|
|
trimEncodingToken(first, last);
|
|
const bool gzip = encodingTokenEquals(first, last, "gzip");
|
|
const bool star = encodingTokenEquals(first, last, "*");
|
|
bool allowed = true;
|
|
for (const char* param = semi; param && param != end;) {
|
|
first = param + 1;
|
|
param = static_cast<const char*>(memchr(first, ';', end - first));
|
|
last = param ? param : end;
|
|
trimEncodingToken(first, last);
|
|
const char* equal = static_cast<const char*>(memchr(first, '=', last - first));
|
|
if (!equal) continue;
|
|
const char* key_end = equal;
|
|
trimEncodingToken(first, key_end);
|
|
if (!encodingTokenEquals(first, key_end, "q")) continue;
|
|
first = equal + 1;
|
|
trimEncodingToken(first, last);
|
|
// qvalue is 0 or 1 with at most three fractional digits; do not let
|
|
// malformed parameters accidentally enable a compressed response.
|
|
allowed = false;
|
|
if (first == last || (*first != '0' && *first != '1')) continue;
|
|
const bool one = *first++ == '1';
|
|
bool nonzero = one;
|
|
bool valid = true;
|
|
if (first != last) {
|
|
if (*first++ != '.' || last - first > 3) valid = false;
|
|
while (first != last) {
|
|
if (*first < '0' || *first > '9' || (one && *first != '0')) valid = false;
|
|
if (*first != '0') nonzero = true;
|
|
++first;
|
|
}
|
|
}
|
|
allowed = valid && nonzero;
|
|
}
|
|
if (gzip) { explicit_gzip = true; gzip_allowed = allowed; }
|
|
if (star) wildcard = allowed;
|
|
item = *end ? end + 1 : end;
|
|
}
|
|
return explicit_gzip ? gzip_allowed : wildcard;
|
|
}
|
|
|
|
} // namespace http
|
|
} // namespace mesh
|