Skip to content
13 changes: 11 additions & 2 deletions libraries/ESP8266WebServer/src/ESP8266WebServer-impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -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>
Expand Down Expand Up @@ -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 = "";
Expand Down
6 changes: 6 additions & 0 deletions libraries/ESP8266WebServer/src/ESP8266WebServer.h
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,8 @@ class ESP8266WebServerTemplate
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);
Expand All @@ -122,6 +124,7 @@ class ESP8266WebServerTemplate
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; }
Expand Down Expand Up @@ -271,6 +274,9 @@ class ESP8266WebServerTemplate
}
}

bool _eTagEnabled = false;
ETagFunction _eTagFunction = nullptr;

protected:
void _addRequestHandler(RequestHandlerType* handler);
void _handleRequest();
Expand Down
70 changes: 56 additions & 14 deletions libraries/ESP8266WebServer/src/detail/RequestHandlersImpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,25 @@

namespace esp8266webserver {

// calculate an eTag for a file in filesystem
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>;
Expand Down Expand Up @@ -92,6 +111,7 @@ class StaticRequestHandler : public RequestHandler<ServerType> {
};


// serve all files within a given directory
template<typename ServerType>
class StaticDirectoryRequestHandler : public StaticRequestHandler<ServerType> {

Expand All @@ -117,6 +137,7 @@ class StaticDirectoryRequestHandler : public StaticRequestHandler<ServerType> {
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;

Expand Down Expand Up @@ -156,17 +177,37 @@ class StaticDirectoryRequestHandler : public StaticRequestHandler<ServerType> {
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;
}

protected:
size_t _baseUriLength;
};


// Serve a specific, single file
template<typename ServerType>
class StaticFileRequestHandler
:
Expand All @@ -180,13 +221,6 @@ public StaticRequestHandler<ServerType> {
:
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 {
Expand All @@ -197,11 +231,17 @@ public StaticRequestHandler<ServerType> {
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;
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") == _eTagCode) {
server.send(304);
return true;
}
}

File f = SRH::_fs.open(SRH::_path, "r");
Expand All @@ -217,14 +257,16 @@ public StaticRequestHandler<ServerType> {
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 as used in http header for this single file
};

} // namespace
Expand Down