Skip to content
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

Supporting ETag http headers on static files #7687

Merged
merged 20 commits into from
Nov 30, 2023
Merged

Supporting ETag http headers on static files #7687

merged 20 commits into from
Nov 30, 2023

Conversation

mathertel
Copy link
Contributor

The ETag (or entity tag) HTTP header specified the version of the returned resource.
This is done by the default implementation by calculating the md5 checksuom of the file content.
A custom ETag calculation can be passed as an option.

This allows client side caching to be more efficient and save bandwidth.

The implementation is taken from esp8266/Arduino and is compatible with the WebServer implementation for esp8266.
where it was implemented by @imwhocodes and me (@mathertel).

The WebServer now include the methon enableETag(bool enable, ETagFunction fn = nullptr)
to enable and disable ETag Headers for a server.

The StaticRequestHandler calculates the ETag value when enabled and checks / populates the header.

An optional ETag calculation function can be passed to enableETag (e.g. to provide different algorithms).

Example

The WebServer Example illustrates using the ETag function.

It is using the "last change date" of the filesystem to create ETags wich is more efficient.

It is available on some file systems implementations (LittleFS, FSFAT but not SPIFFS)
and requires NTP to provide a current datetime.

Some Doku

about http ETags

ETags are "hints" delivered with a web response in the ETag http-Header to the client.

This hint value is added to further requests to the same resource in the .If-None-Match Header.
Hereby the server can check if the client should still use the version in the cache or needs an update.

The web server does not send a full response and file content when the content was not changed
but just returns a 304 Not Modified status to the client resulting in much smaller network packages.

In the cases when the clients will request for the same resources multiple times in a row
the server-side checksum check is a lot more effective than transferring the bytes over the network.

As this is not always true there is an extended option to inject a function that behavior can be customized and tailored to the specific requirements.

enabling ETag support

To enable this in the embedded web server the enableETag() can be used.
(next to enableCORS)

In the simplest version just call enableETag(true) to enable the internal ETag generation that calcs the hint using a md5 checksum in base64 encoded form. This is an simple approach that adds some time for calculation on every request but avoids network traffic.

The headers will look like:

If-None-Match: "GhZka3HevoaEBbtQOgOqlA=="
ETag: "GhZka3HevoaEBbtQOgOqlA=="

ETag support customization

The enableETag() function has an optional second optional parameter to provide a function for ETag calculation of files.

The function enables eTags for all files by using calculating a value from the last write timestamp:

server.enableETag(true, [](FS &fs, const String &path) -> String {
  File f = fs.open(path, "r");
  String eTag = String(f.getLastWrite(), 16);  // use file modification timestamp to create ETag
  f.close();
  return (eTag);
});

The headers will look like:

ETag: "63bbaeb5"
If-None-Match: "63bbaeb5"

@VojtechBartoska VojtechBartoska added Status: Review needed Issue or PR is awaiting review Area: Libraries Issue is related to Library support. labels Jan 25, 2023
@VojtechBartoska
Copy link
Contributor

Thanks for nice and complete PR @mathertel! We'll review it soon.

Copy link
Contributor

@PilnyTomas PilnyTomas left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mathertel nice work!
Would it be easy to add some extended feedback on the file upload? For example in serial output mention the file name and size and/or enable downloading it back.
Also please modify the .ino header and README.md so that it is using our new template
Otherwise LGTM.

@P-R-O-C-H-Y
Copy link
Member

Any updates @mathertel on @PilnyTomas 's comment?

@mathertel
Copy link
Contributor Author

mathertel commented Feb 1, 2023

  • used provided template for readme
  • TRACE output on file upload as requested.
    please review.

@PilnyTomas
Copy link
Contributor

@mathertel Thanks for the addition. Please make sure that when someone tries to upload a too large file it doesn't run away with a looped message:

./components/esp_littlefs/src/littlefs/lfs.c:584:error: No more free space 385
./components/esp_littlefs/src/littlefs/lfs.c:584:error: No more free space 385
./components/esp_littlefs/src/littlefs/lfs.c:584:error: No more free space 385
And to the infinity ...

For small files, the messages are enough.

@mathertel
Copy link
Contributor Author

In the readme:

File upload

By opening http://webserver/$upload.htm you can easily upload files by dragging them over the drop area.

Just take the files from the data folder to create some files that can explore the server functionality.

Files will be uploaded to the root folder of the file system. and you will see it next time using http://webserver/files.htm.

The filesize that is uploaded is not known when the upload mechanism in function
FileServerHandler::upload gets started.

Uploading a file that fits into the available filesystem space
can be found in the Serial output:

starting upload file /file.txt...
finished.
1652 bytes uploaded.

Uploading a file that doesn't fit can be detected while uploading when writing to the filesystem fails.
However upload cannot be aborted by the current handler implementation.

The solution implemented here is to delete the partially uploaded file and wait for the upload ending.
The following can be found in the Serial output:

starting upload file /huge.jpg...
./components/esp_littlefs/src/littlefs/lfs.c:584:error: No more free space 531
  write error!
finished.

You can see on the Serial output that one filesystem write error is reported.

Please be patient and wait for the upload ending even when writing to the filesystem is disabled
it maybe take more than a minute.

@mathertel mathertel requested review from PilnyTomas and removed request for P-R-O-C-H-Y February 7, 2023 17:20
@mathertel
Copy link
Contributor Author

@PilnyTomas : All implemented as you suggested. Please Review or give some feedback.

@mathertel
Copy link
Contributor Author

This is a compatibility request to ESP8266 to easy migration to ESP32 (C3).
Would be welcome to include it in the next version.
@PilnyTomas : All implemented as you suggested. Please Review or give some feedback.
@P-R-O-C-H-Y : Who can do the review when @PilnyTomas doesn't find the time ?

@VojtechBartoska VojtechBartoska added the Type: Example Issue is related to specific example. label Mar 9, 2023
@VojtechBartoska
Copy link
Contributor

hello @mathertel, thanks for the updates. We'll review your PR soon.

Copy link
Contributor

@PilnyTomas PilnyTomas left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi, sorry for the delay, I had some happy changes in my life, so I took a few weeks off.
Thank you for your updates.
I would love to see a different message when the upload fails - instead of "finished" there could be something like "Upload failed"
It isn't necessary - just nice to have, so I approve the PR.

@VojtechBartoska VojtechBartoska added this to the 3.0.0 milestone Apr 4, 2023
@CLAassistant
Copy link

CLAassistant commented May 6, 2023

CLA assistant check
All committers have signed the CLA.

@mathertel mathertel mentioned this pull request May 29, 2023
5 tasks
Copy link
Member

@P-R-O-C-H-Y P-R-O-C-H-Y left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mathertel Please add a empty .skip.esp32h2 file to your example folder to fix failing CI.

@mathertel
Copy link
Contributor Author

@mathertel Please add a empty .skip.esp32h2 file to your example folder to fix failing CI.

done.

Copy link
Collaborator

@lucasssvaz lucasssvaz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Small nitpick but LGTM

</script>
</body>

</html>
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
</html>
</html>

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you fixed index.htm (which was also to be fixed), but forgot to also add a line at the end in files.htm as well

libraries/WebServer/examples/WebServer/data/index.htm Outdated Show resolved Hide resolved
@P-R-O-C-H-Y
Copy link
Member

@mathertel Please fix the missing empty line at the end of files.htm as suggested and LGTM!

@me-no-dev me-no-dev merged commit 89fd90d into espressif:master Nov 30, 2023
38 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Area: Libraries Issue is related to Library support. Status: Review needed Issue or PR is awaiting review Type: Example Issue is related to specific example.
Projects
Development

Successfully merging this pull request may close these issues.

7 participants