Skip to content

Commit 89fd90d

Browse files
mathertelP-R-O-C-H-Ylucasssvaz
authored
Supporting ETag http headers on static files (#7687)
* Supporting ETag http headers on static files * Supporting ETag http headers on static files * WebServer Example and Doku * new template for readme added. * example updated, som more TRACE output. * better TRACE formatting. * upload and trace enhancements * Create .skip.esp32h2 * Update libraries/WebServer/examples/WebServer/data/index.htm Co-authored-by: Lucas Saavedra Vaz <[email protected]> * files.htm trailing newLine --------- Co-authored-by: Jan Procházka <[email protected]> Co-authored-by: Lucas Saavedra Vaz <[email protected]>
1 parent 5fcdb84 commit 89fd90d

File tree

11 files changed

+848
-3
lines changed

11 files changed

+848
-3
lines changed

Diff for: libraries/WebServer/examples/WebServer/.skip.esp32h2

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+

Diff for: libraries/WebServer/examples/WebServer/README.md

+284
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,284 @@
1+
# Arduino-ESP32 WebServer Example for WebServer Library
2+
3+
This example shows different techniques on how to use and extend the WebServer for specific purposes
4+
5+
It is a small project in it's own and has some files to use on the web server to show how to use simple REST based services.
6+
7+
This example requires some space for a filesystem and runs fine boards with 4 MByte flash using the following options:
8+
9+
* Board: ESP32 Dev Module
10+
* Partition Scheme: Default 4MB with spiffs (1.2MB APP/1.5MB SPIFFS)
11+
but LittleFS will be used in the partition (not SPIFFS)
12+
13+
It features
14+
15+
* Setup a web server
16+
* redirect when accessing the url with servername only
17+
* get real time by using builtin NTP functionality
18+
* send HTML responses from Sketch (see builtinfiles.h)
19+
* use a LittleFS file system on the data partition for static files
20+
* use http ETag Header for client side caching of static files
21+
* use custom ETag calculation for static files
22+
* extended FileServerHandler for uploading and deleting static files
23+
* uploading files using drag & drop
24+
* serve APIs using REST services (/api/list, /api/sysinfo)
25+
* define HTML response when no file/api/handler was found
26+
27+
## Supported Targets
28+
29+
Currently, this example supports the following targets.
30+
31+
| Supported Targets | ESP32 | ESP32-S2 | ESP32-C3 |
32+
| ----------------- | ----- | -------- | -------- |
33+
| | yes | yes | yes |
34+
35+
## Use the Example
36+
37+
How to install the Arduino IDE: [Install Arduino IDE](https://github.com/espressif/arduino-esp32/tree/master/docs/arduino-ide).
38+
39+
* In the file `secrets.h` you can add the home WiFi network name ans password.
40+
* Compile and upload to the device.
41+
* Have a look into the monitoring output.
42+
* Open <http://webserver> or <http://(ip-address)> using a browser.
43+
* You will be redirected to <http://webserver/$upload.htm> as there are no files yet in the file system.
44+
* Drag the files from the data folder onto the drop area shown in the browser.
45+
* See below for more details
46+
47+
## Implementing a web server
48+
49+
The WebServer library offers a simple path to implement a web server on a ESP32 based board.
50+
51+
The advantage on using the WebServer instead of the plain simple WiFiServer is that the WebServer
52+
takes much care about the http protocol conventions and features and allows easily access to parameters.
53+
It offers plug-in capabilities by registering specific functionalities that will be outlined below.
54+
55+
### Initialization
56+
57+
In the setup() function in the webserver.ino sketch file the following steps are implemented to make the webserver available on the local network.
58+
59+
* Create a webserver listening to port 80 for http requests.
60+
* Initialize the access to the filesystem in the free flash memory.
61+
* Connect to the local WiFi network. Here is only a straight-forward implementation hard-coding network name and passphrase. You may consider to use something like the WiFiManager library in real applications.
62+
* Register the device in DNS using a known hostname.
63+
* Registering several plug-ins (see below).
64+
* Starting the web server.
65+
66+
### Running
67+
68+
In the loop() function the web server will be given time to receive and send network packages by calling
69+
`server.handleClient();`.
70+
71+
## Registering simple functions to implement RESTful services
72+
73+
Registering function is the simplest integration mechanism available to add functionality. The server offers the `on(path, function)` methods that take the URL and the function as parameters.
74+
75+
There are 2 functions implemented that get registered to handle incoming GET requests for given URLs.
76+
77+
The JSON data format is used often for such services as it is the "natural" data format of the browser using javascript.
78+
79+
When the **handleSysInfo()** function is registered and a browser requests for <http://webserver/api/sysinfo> the function will be called and can collect the requested information.
80+
81+
> ```CPP
82+
> server.on("/api/sysinfo", handleSysInfo);
83+
> ```
84+
85+
The result in this case is a JSON object that is assembled in the result String variable and the returned as a response to the client also giving the information about the data format.
86+
87+
You can try this request in a browser by opening <http://webserver/api/sysinfo> in the address bar.
88+
89+
> ```CPP
90+
> server.on("/api/sysinfo", handleList);
91+
> ```
92+
93+
The function **handleList()** is registered the same way to return the list of files in the file system also returning a JSON object including name, size and the last modification timestamp.
94+
95+
You can try this request in a browser by opening <http://webserver/api/list> in the address bar.
96+
97+
## Registering a function to send out some static content from a String
98+
99+
This is an example of registering a inline function in the web server.
100+
The 2. parameter of the on() method is a so called CPP lamda function (without a name)
101+
that actually has only one line of functionality by sending a string as result to the client.
102+
103+
> ``` cpp
104+
> server.on("/$upload.htm", []() {
105+
> server.send(200, "text/html", FPSTR(uploadContent));
106+
> });
107+
> ```
108+
109+
Here the text from a static String with html code is returned instead of a file from the filesystem.
110+
The content of this string can be found in the file `builtinfiles.h`. It contains a small html+javascript implementation
111+
that allows uploading new files into the empty filesystem.
112+
113+
Just open <http://webserver/$upload.htm> and drag some files from the data folder on the drop area.
114+
115+
## Registering a function to handle requests to the server without a path
116+
117+
Often servers are addressed by using the base URL like <http://webserver/> where no further path details is given.
118+
Of course we like the user to be redirected to something usable. Therefore the `handleRoot()` function is registered:
119+
120+
> ``` cpp
121+
> server.on("/$upload.htm", handleRoot);
122+
> ```
123+
124+
The `handleRoot()` function checks the filesystem for the file named **/index.htm** and creates a redirect to this file when the file exists.
125+
Otherwise the redirection goes to the built-in **/$upload.htm** web page.
126+
127+
## Using the serveStatic plug-in
128+
129+
The **serveStatic** plug in is part of the library and handles delivering files from the filesystem to the client. It can be customized in some ways.
130+
131+
> ``` cpp
132+
> server.enableCORS(true);
133+
> server.enableETag(true);
134+
> server.serveStatic("/", LittleFS, "/");
135+
> ```
136+
137+
### Cross-Origin Ressource Sharing (CORS)
138+
139+
The `enableCORS(true)` function adds a `Access-Control-Allow-Origin: *` http-header to all responses to the client
140+
to inform that it is allowed to call URLs and services on this server from other web sites.
141+
142+
The feature is disabled by default (in the current version) and when you like to disable this then you should call `enableCORS(false)` during setup.
143+
144+
* Web sites providing high sensitive information like online banking this is disabled most of the times.
145+
* Web sites providing advertising information or reusable scripts / images this is enabled.
146+
147+
### enabling ETag support
148+
149+
To enable this in the embedded web server the `enableETag()` can be used.
150+
(next to enableCORS)
151+
152+
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.
153+
154+
The headers will look like:
155+
156+
``` txt
157+
If-None-Match: "GhZka3HevoaEBbtQOgOqlA=="
158+
ETag: "GhZka3HevoaEBbtQOgOqlA=="
159+
```
160+
161+
162+
### ETag support customization
163+
164+
The enableETag() function has an optional second optional parameter to provide a function for ETag calculation of files.
165+
166+
The function enables eTags for all files by using calculating a value from the last write timestamp:
167+
168+
``` cpp
169+
server.enableETag(true, [](FS &fs, const String &path) -> String {
170+
File f = fs.open(path, "r");
171+
String eTag = String(f.getLastWrite(), 16); // use file modification timestamp to create ETag
172+
f.close();
173+
return (eTag);
174+
});
175+
```
176+
177+
The headers will look like:
178+
179+
``` txt
180+
ETag: "63bbaeb5"
181+
If-None-Match: "63bbaeb5"
182+
```
183+
184+
185+
## Registering a full-featured handler as plug-in
186+
187+
The example also implements the class `FileServerHandler` derived from the class `RequestHandler` to plug in functionality
188+
that can handle more complex requests without giving a fixed URL.
189+
It implements uploading and deleting files in the file system that is not implemented by the standard server.serveStatic functionality.
190+
191+
This class has to implements several functions and works in a more detailed way:
192+
193+
* The `canHandle()` method can inspect the given http method and url to decide weather the RequestFileHandler can handle the incoming request or not.
194+
195+
In this case the RequestFileHandler will return true when the request method is an POST for upload or a DELETE for deleting files.
196+
197+
The regular GET requests will be ignored and therefore handled by the also registered server.serveStatic handler.
198+
199+
* The function `handle()` then implements the real deletion of the file.
200+
201+
* The `canUpload()`and `upload()` methods work similar while the `upload()` method is called multiple times to create, append data and close the new file.
202+
203+
## File upload
204+
205+
By opening <http://webserver/$upload.htm> you can easily upload files by dragging them over the drop area.
206+
207+
Just take the files from the data folder to create some files that can explore the server functionality.
208+
209+
Files will be uploaded to the root folder of the file system. and you will see it next time using <http://webserver/files.htm>.
210+
211+
The filesize that is uploaded is not known when the upload mechanism in function
212+
FileServerHandler::upload gets started.
213+
214+
Uploading a file that fits into the available filesystem space
215+
can be found in the Serial output:
216+
217+
``` txt
218+
starting upload file /file.txt...
219+
finished.
220+
1652 bytes uploaded.
221+
```
222+
223+
Uploading a file that doesn't fit can be detected while uploading when writing to the filesystem fails.
224+
However upload cannot be aborted by the current handler implementation.
225+
226+
The solution implemented here is to delete the partially uploaded file and wait for the upload ending.
227+
The following can be found in the Serial output:
228+
229+
``` txt
230+
starting upload file /huge.jpg...
231+
./components/esp_littlefs/src/littlefs/lfs.c:584:error: No more free space 531
232+
write error!
233+
finished.
234+
```
235+
236+
You can see on the Serial output that one filesystem write error is reported.
237+
238+
Please be patient and wait for the upload ending even when writing to the filesystem is disabled
239+
it maybe take more than a minute.
240+
241+
## Registering a special handler for "file not found"
242+
243+
Any other incoming request that was not handled by the registered plug-ins above can be detected by registering
244+
245+
> ``` cpp
246+
> // handle cases when file is not found
247+
> server.onNotFound([]() {
248+
> // standard not found in browser.
249+
> server.send(404, "text/html", FPSTR(notFoundContent));
250+
> });
251+
> ```
252+
253+
This allows sending back an "friendly" result for the browser. Here a simple html page is created from a static string.
254+
You can easily change the html code in the file `builtinfiles.h`.
255+
256+
## customizations
257+
258+
You may like to change the hostname and the timezone in the lines:
259+
260+
> ``` cpp
261+
> #define HOSTNAME "webserver"
262+
> #define TIMEZONE "CET-1CEST,M3.5.0,M10.5.0/3"
263+
> ```
264+
265+
## Troubleshooting
266+
267+
Have a look in the Serial output for some additional runtime information.
268+
269+
## Contribute
270+
271+
To know how to contribute to this project, see [How to contribute.](https://github.com/espressif/arduino-esp32/blob/master/CONTRIBUTING.rst)
272+
273+
If you have any **feedback** or **issue** to report on this example/library, please open an issue or fix it by creating a new PR. Contributions are more than welcome!
274+
275+
Before creating a new issue, be sure to try Troubleshooting and check if the same issue was already created by someone else.
276+
277+
## Resources
278+
279+
* Official ESP32 Forum: [Link](https://esp32.com)
280+
* Arduino-ESP32 Official Repository: [espressif/arduino-esp32](https://github.com/espressif/arduino-esp32)
281+
* ESP32 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32_datasheet_en.pdf)
282+
* ESP32-S2 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-s2_datasheet_en.pdf)
283+
* ESP32-C3 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-c3_datasheet_en.pdf)
284+
* Official ESP-IDF documentation: [ESP-IDF](https://idf.espressif.com)

0 commit comments

Comments
 (0)