Support for pre-compressed and ETag in download#222
Conversation
When downloading file request: 1. **Gzipped file serving**: - Automatically detects and serves pre-compressed `.gz` files when uncompressed originals are missing - Properly sets `Content-Encoding: gzip` headers - Implements `If-None-Match` header comparison for 304 (Not Modified) responses (RFC 7232) - Implements `ETag` header using CRC-32 from gzip trailer (bytes 4-7 from end) - Optimize for speed Changes affect: void AsyncWebServerRequest::send(FS &fs, const String &path, const char *contentType, bool download, AwsTemplateProcessor callback) AsyncWebServerResponse * AsyncWebServerRequest::beginResponse(FS &fs, const String &path, const char *contentType, bool download, AwsTemplateProcessor callback) AsyncFileResponse::AsyncFileResponse(FS &fs, const String &path, const char *contentType, bool download, AwsTemplateProcessor callback) : AsyncAbstractResponse(callback)
|
Implemented as #219 |
There was a problem hiding this comment.
Pull Request Overview
This PR enhances file downloads by automatically serving pre-compressed .gz files when originals are missing, adding gzip headers, and implementing CRC32-based ETag handling for 304 responses.
- Fallback to gzip-compressed file in
AsyncFileResponse, settingContent-Encoding,ETag, andCache-Control. - Refactor
AsyncWebServerRequest::send()to check uncompressed first, handle compressed files, and respond304 Not Modifiedwhen ETags match. - Simplify Content-Disposition logic for inline vs. download modes.
Reviewed Changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| src/WebResponses.cpp | Updated AsyncFileResponse constructor to support gzip fallback, ETag generation, and download/inline headers. |
| src/AsyncWebServerRequest.cpp | Refactored send() to pre-check uncompressed files, open and validate .gz files, and implement ETag-based 304 logic. |
Comments suppressed due to low confidence (1)
src/WebResponses.cpp:716
- No tests cover the scenario where a corrupted or invalid gzip file triggers a 404 response; add unit tests to validate this path.
_code = 404;
| if (file && file.size() >= 18) { // 18 is the minimum size of valid gzip file | ||
| file.seek(file.size() - 8); | ||
| // Handle compressed version | ||
| const String gzPath = path + asyncsrv::T__gz; |
There was a problem hiding this comment.
The download flag is ignored when serving compressed files. You should skip serving the .gz version if download is true to respect the download mode.
| const String gzPath = path + asyncsrv::T__gz; | |
| const String gzPath = path + asyncsrv::T__gz; | |
| // Skip serving compressed file if download is true | |
| if (download) { | |
| send(404); | |
| return; | |
| } |
|
Thanks @JosePineiro ! Let's get this PR merged, then I will cut a new release. |
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Tomorrow I'll submit another PR with a speed improvement for the _setContentTypeFromPath function. |
When downloading file request:
.gzfiles when uncompressed originals are missingContent-Encoding: gzipheadersIf-None-Matchheader comparison for 304 (Not Modified) responses (RFC 7232)ETagheader using CRC-32 from gzip trailer (bytes 4-7 from end)void AsyncWebServerRequest::send(FS &fs, const String &path, const char *contentType, bool download, AwsTemplateProcessor callback) AsyncWebServerResponse *
AsyncWebServerRequest::beginResponse(FS &fs, const String &path, const char *contentType, bool download, AwsTemplateProcessor callback)
AsyncFileResponse::AsyncFileResponse(FS &fs, const String &path, const char *contentType, bool download, AwsTemplateProcessor callback)
: AsyncAbstractResponse(callback)