-
Notifications
You must be signed in to change notification settings - Fork 67
Support for pre-compressed and ETag in download #222
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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;
src/AsyncWebServerRequest.cpp
Outdated
| 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; |
Copilot
AI
Jul 9, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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; | |
| } |
Copilot uses AI. Check for mistakes.
|
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)