mirror of
https://github.com/esp8266/Arduino.git
synced 2025-06-12 01:53:07 +03:00
Improvements to the existing ETag implementation (#8227)
* WebServer eTag implementation improvements
This commit is contained in:
@ -68,6 +68,13 @@ template <typename ServerType>
|
||||
void ESP8266WebServerTemplate<ServerType>::enableCORS(bool enable) {
|
||||
_corsEnabled = enable;
|
||||
}
|
||||
|
||||
template <typename ServerType>
|
||||
void ESP8266WebServerTemplate<ServerType>::enableETag(bool enable, ETagFunction fn) {
|
||||
_eTagEnabled = enable;
|
||||
_eTagFunction = fn;
|
||||
}
|
||||
|
||||
template <typename ServerType>
|
||||
void ESP8266WebServerTemplate<ServerType>::begin() {
|
||||
close();
|
||||
@ -264,10 +271,11 @@ void ESP8266WebServerTemplate<ServerType>::serveStatic(const char* uri, FS& fs,
|
||||
file.close();
|
||||
}
|
||||
|
||||
if(is_file)
|
||||
if(is_file) {
|
||||
_addRequestHandler(new StaticFileRequestHandler<ServerType>(fs, path, uri, cache_header));
|
||||
else
|
||||
} else {
|
||||
_addRequestHandler(new StaticDirectoryRequestHandler<ServerType>(fs, path, uri, cache_header));
|
||||
}
|
||||
}
|
||||
|
||||
template <typename ServerType>
|
||||
@ -436,6 +444,7 @@ void ESP8266WebServerTemplate<ServerType>::_prepareHeader(String& response, int
|
||||
sendHeader(String(F("Keep-Alive")), String(F("timeout=")) + HTTP_MAX_CLOSE_WAIT);
|
||||
}
|
||||
|
||||
|
||||
response += _responseHeaders;
|
||||
response += "\r\n";
|
||||
_responseHeaders = "";
|
||||
|
@ -114,6 +114,8 @@ public:
|
||||
void requestAuthentication(HTTPAuthMethod mode = BASIC_AUTH, const char* realm = NULL, const String& authFailMsg = String("") );
|
||||
|
||||
typedef std::function<void(void)> THandlerFunction;
|
||||
typedef std::function<String(FS &fs, const String &fName)> ETagFunction;
|
||||
|
||||
void on(const Uri &uri, THandlerFunction handler);
|
||||
void on(const Uri &uri, HTTPMethod method, THandlerFunction fn);
|
||||
void on(const Uri &uri, HTTPMethod method, THandlerFunction fn, THandlerFunction ufn);
|
||||
@ -122,6 +124,7 @@ public:
|
||||
void onNotFound(THandlerFunction fn); //called when handler is not assigned
|
||||
void onFileUpload(THandlerFunction fn); //handle file uploads
|
||||
void enableCORS(bool enable);
|
||||
void enableETag(bool enable, ETagFunction fn = nullptr);
|
||||
|
||||
const String& uri() const { return _currentUri; }
|
||||
HTTPMethod method() const { return _currentMethod; }
|
||||
@ -271,6 +274,9 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
bool _eTagEnabled = false;
|
||||
ETagFunction _eTagFunction = nullptr;
|
||||
|
||||
protected:
|
||||
void _addRequestHandler(RequestHandlerType* handler);
|
||||
void _handleRequest();
|
||||
|
@ -9,6 +9,26 @@
|
||||
|
||||
namespace esp8266webserver {
|
||||
|
||||
// calculate an ETag for a file in filesystem based on md5 checksum
|
||||
// that can be used in the http headers - include quotes.
|
||||
static String calcETag(FS &fs, const String &path) {
|
||||
String result;
|
||||
|
||||
// calculate eTag using md5 checksum
|
||||
uint8_t md5_buf[16];
|
||||
File f = fs.open(path, "r");
|
||||
MD5Builder calcMD5;
|
||||
calcMD5.begin();
|
||||
calcMD5.addStream(f, f.size());
|
||||
calcMD5.calculate();
|
||||
calcMD5.getBytes(md5_buf);
|
||||
f.close();
|
||||
// create a minimal-length eTag using base64 byte[]->text encoding.
|
||||
result = "\"" + base64::encode(md5_buf, 16, false) + "\"";
|
||||
return(result);
|
||||
} // calcETag
|
||||
|
||||
|
||||
template<typename ServerType>
|
||||
class FunctionRequestHandler : public RequestHandler<ServerType> {
|
||||
using WebServerType = ESP8266WebServerTemplate<ServerType>;
|
||||
@ -92,6 +112,7 @@ protected:
|
||||
};
|
||||
|
||||
|
||||
// serve all files within a given directory
|
||||
template<typename ServerType>
|
||||
class StaticDirectoryRequestHandler : public StaticRequestHandler<ServerType> {
|
||||
|
||||
@ -117,6 +138,7 @@ public:
|
||||
DEBUGV("DirectoryRequestHandler::handle: request=%s _uri=%s\r\n", requestUri.c_str(), SRH::_uri.c_str());
|
||||
|
||||
String path;
|
||||
String eTagCode;
|
||||
path.reserve(SRH::_path.length() + requestUri.length() + 32);
|
||||
path = SRH::_path;
|
||||
|
||||
@ -156,10 +178,28 @@ public:
|
||||
return false;
|
||||
}
|
||||
|
||||
if (server._eTagEnabled) {
|
||||
if (server._eTagFunction) {
|
||||
eTagCode = (server._eTagFunction)(SRH::_fs, path);
|
||||
} else {
|
||||
eTagCode = esp8266webserver::calcETag(SRH::_fs, path);
|
||||
}
|
||||
|
||||
if (server.header("If-None-Match") == eTagCode) {
|
||||
server.send(304);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (SRH::_cache_header.length() != 0)
|
||||
server.sendHeader("Cache-Control", SRH::_cache_header);
|
||||
|
||||
if ((server._eTagEnabled) && (eTagCode.length() > 0)) {
|
||||
server.sendHeader("ETag", eTagCode);
|
||||
}
|
||||
|
||||
server.streamFile(f, contentType, requestMethod);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -167,6 +207,8 @@ protected:
|
||||
size_t _baseUriLength;
|
||||
};
|
||||
|
||||
|
||||
// Serve a specific, single file
|
||||
template<typename ServerType>
|
||||
class StaticFileRequestHandler
|
||||
:
|
||||
@ -180,13 +222,6 @@ public:
|
||||
:
|
||||
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 {
|
||||
@ -197,11 +232,17 @@ public:
|
||||
if (!canHandle(requestMethod, requestUri))
|
||||
return false;
|
||||
|
||||
const String etag = "\"" + base64::encode(_ETag_md5, 16, false) + "\"";
|
||||
if (server._eTagEnabled) {
|
||||
if (server._eTagFunction) {
|
||||
_eTagCode = (server._eTagFunction)(SRH::_fs, SRH::_path);
|
||||
} else if (_eTagCode.isEmpty()) {
|
||||
_eTagCode = esp8266webserver::calcETag(SRH::_fs, SRH::_path);
|
||||
}
|
||||
|
||||
if(server.header("If-None-Match") == etag){
|
||||
server.send(304);
|
||||
return true;
|
||||
if (server.header("If-None-Match") == _eTagCode) {
|
||||
server.send(304);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
File f = SRH::_fs.open(SRH::_path, "r");
|
||||
@ -217,14 +258,16 @@ public:
|
||||
if (SRH::_cache_header.length() != 0)
|
||||
server.sendHeader("Cache-Control", SRH::_cache_header);
|
||||
|
||||
server.sendHeader("ETag", etag);
|
||||
if ((server._eTagEnabled) && (_eTagCode.length() > 0)) {
|
||||
server.sendHeader("ETag", _eTagCode);
|
||||
}
|
||||
|
||||
server.streamFile(f, mime::getContentType(SRH::_path), requestMethod);
|
||||
return true;
|
||||
}
|
||||
|
||||
protected:
|
||||
uint8_t _ETag_md5[16];
|
||||
String _eTagCode; // ETag code calculated for this file as used in http header include quotes.
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
Reference in New Issue
Block a user