11 #include < ESPAsyncWebServer.h>
22
3+ /* *
4+ * @brief Sends a file from the filesystem to the client, with optional gzip compression and ETag-based caching.
5+ *
6+ * This method serves files over HTTP from the provided filesystem. If a compressed version of the file
7+ * (with a `.gz` extension) exists and the `download` flag is not set, it serves the compressed file.
8+ * It also handles ETag caching using the CRC32 value from the gzip trailer, responding with `304 Not Modified`
9+ * if the client's `If-None-Match` header matches the generated ETag.
10+ *
11+ * @param fs Reference to the filesystem (SPIFFS, LittleFS, etc.).
12+ * @param path Path to the file to be served.
13+ * @param contentType Optional MIME type of the file to be sent.
14+ * If contentType is "" it will be obtained from the file extension
15+ * @param download If true, forces the file to be sent as a download (disables gzip compression).
16+ * @param callback Optional template processor for dynamic content generation.
17+ * Templates will not be processed in compressed files.
18+ *
19+ * @note If neither the file nor its compressed version exists, responds with `404 Not Found`.
20+ */
321 void AsyncWebServerRequest::send (FS &fs, const String &path, const char *contentType, bool download, AwsTemplateProcessor callback) {
422 const String gzPath = path + asyncsrv::T__gz;
523 const bool useCompressedVersion = !download && fs.exists (gzPath);
1331
1432 uint8_t crcFromGzipTrailer[4 ];
1533 if (file.read (crcFromGzipTrailer, sizeof (crcFromGzipTrailer)) == sizeof (crcFromGzipTrailer)) {
16- char serverETag[11 ]; // " + 8 hex chars + " + '\0'
34+ char serverETag[9 ];
1735 _getEtag (crcFromGzipTrailer, serverETag);
1836
1937 // Compare with client's If-None-Match header
4260 * This function converts a 4-byte array into a hexadecimal ETag string enclosed in quotes.
4361 *
4462 * @param trailer[4] Input array of 4 bytes to convert to hexadecimal
45- * @param serverETag Output buffer that must be at least 11 bytes long to store the ETag
46- * Must be pre-allocated with minimum 11 bytes (8 hex + 2 quotes + 1 null terminator)
63+ * @param serverETag Output buffer to store the ETag
64+ * Must be pre-allocated with minimum 9 bytes (8 hex + 1 null terminator)
4765 */
4866void AsyncWebServerRequest::_getEtag (uint8_t trailer[4 ], char * serverETag) {
4967 static constexpr char hexChars[] = " 0123456789ABCDEF" ;
5068
5169 uint32_t data;
5270 memcpy (&data, trailer, 4 );
5371
54- serverETag[0 ] = ' "' ;
55- serverETag[1 ] = hexChars[(data >> 4 ) & 0x0F ];
56- serverETag[2 ] = hexChars[data & 0x0F ];
57- serverETag[3 ] = hexChars[(data >> 12 ) & 0x0F ];
58- serverETag[4 ] = hexChars[(data >> 8 ) & 0x0F ];
59- serverETag[5 ] = hexChars[(data >> 20 ) & 0x0F ];
60- serverETag[6 ] = hexChars[(data >> 16 ) & 0x0F ];
61- serverETag[7 ] = hexChars[(data >> 28 )];
62- serverETag[8 ] = hexChars[(data >> 24 ) & 0x0F ];
63- serverETag[9 ] = ' "' ;
64- serverETag[10 ] = ' \0 ' ;
72+ serverETag[0 ] = hexChars[(data >> 4 ) & 0x0F ];
73+ serverETag[1 ] = hexChars[data & 0x0F ];
74+ serverETag[2 ] = hexChars[(data >> 12 ) & 0x0F ];
75+ serverETag[3 ] = hexChars[(data >> 8 ) & 0x0F ];
76+ serverETag[4 ] = hexChars[(data >> 20 ) & 0x0F ];
77+ serverETag[5 ] = hexChars[(data >> 16 ) & 0x0F ];
78+ serverETag[6 ] = hexChars[(data >> 28 )];
79+ serverETag[7 ] = hexChars[(data >> 24 ) & 0x0F ];
80+ serverETag[8 ] = ' \0 ' ;
6581}
0 commit comments