Fix path problems in static handler and improve performance. (#41)
This commit is contained in:
parent
f384cd1f76
commit
a52873b451
|
@ -26,32 +26,43 @@
|
||||||
|
|
||||||
class AsyncStaticWebHandler: public AsyncWebHandler {
|
class AsyncStaticWebHandler: public AsyncWebHandler {
|
||||||
private:
|
private:
|
||||||
String _getPath(AsyncWebServerRequest *request);
|
String _getPath(AsyncWebServerRequest *request, const bool withStats);
|
||||||
|
bool _fileExists(const String path, const bool withStats);
|
||||||
|
uint8_t _countBits(const uint8_t value);
|
||||||
protected:
|
protected:
|
||||||
FS _fs;
|
FS _fs;
|
||||||
String _uri;
|
String _uri;
|
||||||
String _path;
|
String _path;
|
||||||
String _cache_header;
|
String _cache_header;
|
||||||
bool _isFile;
|
bool _isDir;
|
||||||
|
bool _gzipFirst;
|
||||||
|
uint8_t _gzipStats;
|
||||||
|
uint8_t _fileStats;
|
||||||
public:
|
public:
|
||||||
AsyncStaticWebHandler(FS& fs, const char* path, const char* uri, const char* cache_header)
|
AsyncStaticWebHandler(FS& fs, const char* path, const char* uri, const char* cache_header)
|
||||||
: _fs(fs), _uri(uri), _path(path), _cache_header(cache_header){
|
: _fs(fs), _uri(uri), _path(path), _cache_header(cache_header){
|
||||||
|
// Ensure leading '/'
|
||||||
|
if (_uri.length() == 0 || _uri[0] != '/') _uri = "/" + _uri;
|
||||||
|
if (_path.length() == 0 || _path[0] != '/') _path = "/" + _path;
|
||||||
|
|
||||||
_isFile = _fs.exists(path) || _fs.exists((String(path)+".gz").c_str());
|
// If uri or path ends with '/' we assume a hint that this is a directory to improve performance.
|
||||||
if (_uri != "/" && _uri.endsWith("/")) {
|
// However - if they both do not end '/' we, can't assume they are files, they can still be directory.
|
||||||
_uri = _uri.substring(0, _uri.length() - 1);
|
bool isUriDir = _uri[_uri.length()-1] == '/';
|
||||||
DEBUGF("[AsyncStaticWebHandler] _uri / removed\n");
|
bool isPathDir = _path[_path.length()-1] == '/';
|
||||||
}
|
_isDir = isUriDir || isPathDir;
|
||||||
if (_path != "/" && _path.endsWith("/")) {
|
|
||||||
_path = _path.substring(0, _path.length() - 1);
|
|
||||||
DEBUGF("[AsyncStaticWebHandler] _path / removed\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// If we serving directory - remove the trailing '/' so we can handle default file
|
||||||
|
// Notice that root will be "" not "/"
|
||||||
|
if (_isDir && isUriDir) _uri = _uri.substring(0, _uri.length()-1);
|
||||||
|
if (_isDir && isPathDir) _path = _path.substring(0, _path.length()-1);
|
||||||
|
|
||||||
|
// Reset stats
|
||||||
|
_gzipFirst = false;
|
||||||
|
_gzipStats = 0;
|
||||||
|
_fileStats = 0;
|
||||||
}
|
}
|
||||||
bool canHandle(AsyncWebServerRequest *request);
|
bool canHandle(AsyncWebServerRequest *request);
|
||||||
void handleRequest(AsyncWebServerRequest *request);
|
void handleRequest(AsyncWebServerRequest *request);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class AsyncCallbackWebHandler: public AsyncWebHandler {
|
class AsyncCallbackWebHandler: public AsyncWebHandler {
|
||||||
|
|
|
@ -23,66 +23,81 @@
|
||||||
|
|
||||||
bool AsyncStaticWebHandler::canHandle(AsyncWebServerRequest *request)
|
bool AsyncStaticWebHandler::canHandle(AsyncWebServerRequest *request)
|
||||||
{
|
{
|
||||||
if (request->method() != HTTP_GET) {
|
if (request->method() == HTTP_GET &&
|
||||||
return false;
|
request->url().startsWith(_uri) &&
|
||||||
}
|
_getPath(request, true).length()) {
|
||||||
if ((_isFile && request->url() != _uri) ) {
|
|
||||||
return false;
|
DEBUGF("[AsyncStaticWebHandler::canHandle] TRUE\n");
|
||||||
}
|
return true;
|
||||||
// if the root of the request matches the _uri then it checks to see if there is a file it can handle.
|
|
||||||
if (request->url().startsWith(_uri)) {
|
|
||||||
String path = _getPath(request);
|
|
||||||
if (_fs.exists(path) || _fs.exists(path + ".gz")) {
|
|
||||||
DEBUGF("[AsyncStaticWebHandler::canHandle] TRUE\n");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
String AsyncStaticWebHandler::_getPath(AsyncWebServerRequest *request)
|
String AsyncStaticWebHandler::_getPath(AsyncWebServerRequest *request, const bool withStats)
|
||||||
{
|
{
|
||||||
|
// Remove the found uri
|
||||||
|
String path = request->url().substring(_uri.length());
|
||||||
|
|
||||||
String path = request->url();
|
// We can skip the file check if we serving a directory and (we have full match or we end with '/')
|
||||||
DEBUGF("[AsyncStaticWebHandler::_getPath]\n");
|
bool canSkipFileCheck = _isDir && (path.length() == 0 || path[path.length()-1] == '/');
|
||||||
DEBUGF(" [stored] _uri = %s, _path = %s\n" , _uri.c_str(), _path.c_str() ) ;
|
|
||||||
DEBUGF(" [request] url = %s\n", request->url().c_str() );
|
|
||||||
|
|
||||||
if (!_isFile) {
|
path = _path + path;
|
||||||
DEBUGF(" _isFile = false\n");
|
|
||||||
String baserequestUrl = request->url().substring(_uri.length()); // this is the request - stored _uri... /espman/
|
|
||||||
DEBUGF(" baserequestUrl = %s\n", baserequestUrl.c_str());
|
|
||||||
|
|
||||||
if (!baserequestUrl.length()) {
|
// Do we have a file or .gz file
|
||||||
baserequestUrl += "/";
|
if (!canSkipFileCheck) if (_fileExists(path, withStats)) return path;
|
||||||
}
|
|
||||||
|
|
||||||
path = _path + baserequestUrl;
|
// Try to add default page, ensure there is a trailing '/' ot the path.
|
||||||
DEBUGF(" path = path + baserequestUrl, path = %s\n", path.c_str());
|
if (path.length() == 0 || path[path.length()-1] != '/') path += "/";
|
||||||
|
path += "index.htm";
|
||||||
|
|
||||||
if (path.endsWith("/")) {
|
if (_fileExists(path, withStats)) return path;
|
||||||
DEBUGF(" 3 path ends with / : path = index.htm \n");
|
|
||||||
path += "index.htm";
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
path = _path;
|
|
||||||
}
|
|
||||||
|
|
||||||
DEBUGF(" final path = %s\n", path.c_str());
|
// No file - return empty string
|
||||||
DEBUGF("[AsyncStaticWebHandler::_getPath] END\n\n");
|
return String();
|
||||||
|
|
||||||
return path;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool AsyncStaticWebHandler::_fileExists(const String path, const bool withStats)
|
||||||
|
{
|
||||||
|
bool fileFound = false;
|
||||||
|
bool gzipFound = false;
|
||||||
|
|
||||||
|
String gzip = path + ".gz";
|
||||||
|
|
||||||
|
if (_gzipFirst) {
|
||||||
|
gzipFound = _fs.exists(gzip);
|
||||||
|
if (!gzipFound) fileFound = _fs.exists(path);
|
||||||
|
} else {
|
||||||
|
fileFound = _fs.exists(path);
|
||||||
|
if (!fileFound) gzipFound = _fs.exists(gzip);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool found = fileFound || gzipFound;
|
||||||
|
|
||||||
|
if (withStats && found) {
|
||||||
|
_gzipStats = (_gzipStats << 1) + gzipFound ? 1 : 0;
|
||||||
|
_fileStats = (_fileStats << 1) + fileFound ? 1 : 0;
|
||||||
|
_gzipFirst = _countBits(_gzipStats) > _countBits(_fileStats);
|
||||||
|
}
|
||||||
|
|
||||||
|
return found;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t AsyncStaticWebHandler::_countBits(const uint8_t value)
|
||||||
|
{
|
||||||
|
uint8_t w = value;
|
||||||
|
uint8_t n;
|
||||||
|
for (n=0; w!=0; n++) w&=w-1;
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
void AsyncStaticWebHandler::handleRequest(AsyncWebServerRequest *request)
|
void AsyncStaticWebHandler::handleRequest(AsyncWebServerRequest *request)
|
||||||
{
|
{
|
||||||
|
String path = _getPath(request, false);
|
||||||
|
|
||||||
String path = _getPath(request);
|
if (path.length()) {
|
||||||
|
AsyncWebServerResponse * response = new AsyncFileResponse(_fs, path);
|
||||||
if (_fs.exists(path) || _fs.exists(path + ".gz")) {
|
|
||||||
AsyncWebServerResponse * response = request->beginResponse(_fs, path);
|
|
||||||
if (_cache_header.length() != 0)
|
if (_cache_header.length() != 0)
|
||||||
response->addHeader("Cache-Control", _cache_header);
|
response->addHeader("Cache-Control", _cache_header);
|
||||||
request->send(response);
|
request->send(response);
|
||||||
|
@ -90,6 +105,4 @@ void AsyncStaticWebHandler::handleRequest(AsyncWebServerRequest *request)
|
||||||
request->send(404);
|
request->send(404);
|
||||||
}
|
}
|
||||||
path = String();
|
path = String();
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue