1
0
mirror of https://github.com/esp8266/Arduino.git synced 2025-08-26 01:44:17 +03:00

ETag support for WebServer (#7709)

* implemented with native md5
* testing locals variables
* less memory used, partil refactoring
* reworked serveStatic logic, different handler for File and Directory
This commit is contained in:
Luca Passarella
2020-12-01 09:52:58 +01:00
committed by GitHub
parent 2e4563c76b
commit 7a368747e0
3 changed files with 162 additions and 86 deletions

View File

@@ -72,83 +72,11 @@ public:
, _path(path)
, _cache_header(cache_header)
{
if (fs.exists(path)) {
File file = fs.open(path, "r");
_isFile = file && file.isFile();
file.close();
}
else {
_isFile = false;
}
DEBUGV("StaticRequestHandler: path=%s uri=%s isFile=%d, cache_header=%s\r\n", path, uri, _isFile, cache_header == __null ? "" : cache_header);
_baseUriLength = _uri.length();
DEBUGV("StaticRequestHandler: path=%s uri=%s, cache_header=%s\r\n", path, uri, cache_header == __null ? "" : cache_header);
}
bool canHandle(HTTPMethod requestMethod, const String& requestUri) override {
if ((requestMethod != HTTP_GET) && (requestMethod != HTTP_HEAD))
return false;
if ((_isFile && requestUri != _uri) || !requestUri.startsWith(_uri))
return false;
return true;
}
bool handle(WebServerType& server, HTTPMethod requestMethod, const String& requestUri) override {
if (!canHandle(requestMethod, requestUri))
return false;
DEBUGV("StaticRequestHandler::handle: request=%s _uri=%s\r\n", requestUri.c_str(), _uri.c_str());
String path;
path.reserve(_path.length() + requestUri.length() + 32);
path = _path;
if (!_isFile) {
// Append whatever follows this URI in request to get the file path.
path += requestUri.substring(_baseUriLength);
// Base URI doesn't point to a file.
// If a directory is requested, look for index file.
if (path.endsWith("/"))
path += F("index.htm");
// If neither <blah> nor <blah>.gz exist, and <blah> is a file.htm, try it with file.html instead
// For the normal case this will give a search order of index.htm, index.htm.gz, index.html, index.html.gz
if (!_fs.exists(path) && !_fs.exists(path + ".gz") && path.endsWith(".htm")) {
path += 'l';
}
}
DEBUGV("StaticRequestHandler::handle: path=%s, isFile=%d\r\n", path.c_str(), _isFile);
String contentType = mime::getContentType(path);
using namespace mime;
// look for gz file, only if the original specified path is not a gz. So part only works to send gzip via content encoding when a non compressed is asked for
// if you point the the path to gzip you will serve the gzip as content type "application/x-gzip", not text or javascript etc...
if (!path.endsWith(FPSTR(mimeTable[gz].endsWith)) && !_fs.exists(path)) {
String pathWithGz = path + FPSTR(mimeTable[gz].endsWith);
if(_fs.exists(pathWithGz))
path += FPSTR(mimeTable[gz].endsWith);
}
File f = _fs.open(path, "r");
if (!f)
return false;
if (!f.isFile()) {
f.close();
return false;
}
if (_cache_header.length() != 0)
server.sendHeader("Cache-Control", _cache_header);
server.streamFile(f, contentType, requestMethod);
return true;
bool validMethod(HTTPMethod requestMethod){
return (requestMethod == HTTP_GET) || (requestMethod == HTTP_HEAD);
}
/* Deprecated version. Please use mime::getContentType instead */
@@ -161,10 +89,144 @@ protected:
String _uri;
String _path;
String _cache_header;
bool _isFile;
};
template<typename ServerType>
class StaticDirectoryRequestHandler : public StaticRequestHandler<ServerType> {
using SRH = StaticRequestHandler<ServerType>;
using WebServerType = ESP8266WebServerTemplate<ServerType>;
public:
StaticDirectoryRequestHandler(FS& fs, const char* path, const char* uri, const char* cache_header)
:
SRH(fs, path, uri, cache_header),
_baseUriLength{SRH::_uri.length()}
{}
bool canHandle(HTTPMethod requestMethod, const String& requestUri) override {
return SRH::validMethod(requestMethod) && requestUri.startsWith(SRH::_uri);
}
bool handle(WebServerType& server, HTTPMethod requestMethod, const String& requestUri) override {
if (!canHandle(requestMethod, requestUri))
return false;
DEBUGV("DirectoryRequestHandler::handle: request=%s _uri=%s\r\n", requestUri.c_str(), SRH::_uri.c_str());
String path;
path.reserve(SRH::_path.length() + requestUri.length() + 32);
path = SRH::_path;
// Append whatever follows this URI in request to get the file path.
path += requestUri.substring(_baseUriLength);
// Base URI doesn't point to a file.
// If a directory is requested, look for index file.
if (path.endsWith("/"))
path += F("index.htm");
// If neither <blah> nor <blah>.gz exist, and <blah> is a file.htm, try it with file.html instead
// For the normal case this will give a search order of index.htm, index.htm.gz, index.html, index.html.gz
if (!SRH::_fs.exists(path) && !SRH::_fs.exists(path + ".gz") && path.endsWith(".htm")) {
path += 'l';
}
DEBUGV("DirectoryRequestHandler::handle: path=%s\r\n", path.c_str());
String contentType = mime::getContentType(path);
using namespace mime;
// look for gz file, only if the original specified path is not a gz. So part only works to send gzip via content encoding when a non compressed is asked for
// if you point the the path to gzip you will serve the gzip as content type "application/x-gzip", not text or javascript etc...
if (!path.endsWith(FPSTR(mimeTable[gz].endsWith)) && !SRH::_fs.exists(path)) {
String pathWithGz = path + FPSTR(mimeTable[gz].endsWith);
if(SRH::_fs.exists(pathWithGz))
path += FPSTR(mimeTable[gz].endsWith);
}
File f = SRH::_fs.open(path, "r");
if (!f)
return false;
if (!f.isFile()) {
f.close();
return false;
}
if (SRH::_cache_header.length() != 0)
server.sendHeader("Cache-Control", SRH::_cache_header);
server.streamFile(f, contentType, requestMethod);
return true;
}
protected:
size_t _baseUriLength;
};
template<typename ServerType>
class StaticFileRequestHandler
:
public StaticRequestHandler<ServerType> {
using SRH = StaticRequestHandler<ServerType>;
using WebServerType = ESP8266WebServerTemplate<ServerType>;
public:
StaticFileRequestHandler(FS& fs, const char* path, const char* uri, const char* cache_header)
:
StaticRequestHandler<ServerType>{fs, path, uri, cache_header}
{
File f = SRH::_fs.open(path, "r");
MD5Builder calcMD5;
calcMD5.begin();
calcMD5.addStream(f, f.size());
calcMD5.calculate();
calcMD5.getBytes(_ETag_md5);
f.close();
}
bool canHandle(HTTPMethod requestMethod, const String& requestUri) override {
return SRH::validMethod(requestMethod) && requestUri == SRH::_uri;
}
bool handle(WebServerType& server, HTTPMethod requestMethod, const String & requestUri) override {
if (!canHandle(requestMethod, requestUri))
return false;
const String etag = "\"" + base64::encode(_ETag_md5, 16, false) + "\"";
if(server.header("If-None-Match") == etag){
server.send(304);
return true;
}
File f = SRH::_fs.open(SRH::_path, "r");
if (!f)
return false;
if (!f.isFile()) {
f.close();
return false;
}
if (SRH::_cache_header.length() != 0)
server.sendHeader("Cache-Control", SRH::_cache_header);
server.sendHeader("ETag", etag);
server.streamFile(f, mime::getContentType(SRH::_path), requestMethod);
return true;
}
protected:
uint8_t _ETag_md5[16];
};
} // namespace
#endif //REQUESTHANDLERSIMPL_H
#endif //REQUESTHANDLERSIMPL_H