mirror of
https://github.com/Laxilef/OTGateway.git
synced 2025-12-11 02:34:29 +05:00
224 lines
6.1 KiB
C++
224 lines
6.1 KiB
C++
#include <FS.h>
|
|
|
|
|
|
class DynamicPage : public RequestHandler {
|
|
public:
|
|
typedef std::function<bool(HTTPMethod, const String&)> CanHandleCallback;
|
|
typedef std::function<bool()> BeforeSendCallback;
|
|
typedef std::function<String(const char*)> TemplateCallback;
|
|
|
|
DynamicPage(const char* uri, FS* fs, const char* path, const char* cacheHeader = nullptr) {
|
|
this->uri = uri;
|
|
this->fs = fs;
|
|
this->path = path;
|
|
this->cacheHeader = cacheHeader;
|
|
}
|
|
|
|
DynamicPage* setCanHandleCallback(CanHandleCallback callback = nullptr) {
|
|
this->canHandleCallback = callback;
|
|
|
|
return this;
|
|
}
|
|
|
|
DynamicPage* setBeforeSendCallback(BeforeSendCallback callback = nullptr) {
|
|
this->beforeSendCallback = callback;
|
|
|
|
return this;
|
|
}
|
|
|
|
DynamicPage* setTemplateCallback(TemplateCallback callback = nullptr) {
|
|
this->templateCallback = callback;
|
|
|
|
return this;
|
|
}
|
|
|
|
#if defined(ARDUINO_ARCH_ESP32)
|
|
bool canHandle(WebServer &server, HTTPMethod method, const String &uri) override {
|
|
return this->canHandle(method, uri);
|
|
}
|
|
#endif
|
|
|
|
bool canHandle(HTTPMethod method, const String& uri) override {
|
|
return uri.equals(this->uri) && (!this->canHandleCallback || this->canHandleCallback(method, uri));
|
|
}
|
|
|
|
bool handle(WebServer& server, HTTPMethod method, const String& uri) override {
|
|
if (!this->canHandle(method, uri)) {
|
|
return false;
|
|
}
|
|
|
|
if (this->beforeSendCallback && !this->beforeSendCallback()) {
|
|
return true;
|
|
}
|
|
|
|
File file = this->fs->open(this->path, "r");
|
|
if (!file) {
|
|
return false;
|
|
|
|
} else if (file.isDirectory()) {
|
|
file.close();
|
|
return false;
|
|
}
|
|
|
|
if (this->cacheHeader != nullptr) {
|
|
server.sendHeader(F("Cache-Control"), this->cacheHeader);
|
|
}
|
|
|
|
#ifdef ARDUINO_ARCH_ESP8266
|
|
if (!server.chunkedResponseModeStart(200, F("text/html"))) {
|
|
server.send(505, F("text/html"), F("HTTP1.1 required"));
|
|
return true;
|
|
}
|
|
#else
|
|
server.setContentLength(CONTENT_LENGTH_UNKNOWN);
|
|
server.send(200, "text/html", emptyString);
|
|
#endif
|
|
|
|
uint8_t* argStartPos = nullptr;
|
|
uint8_t* argEndPos = nullptr;
|
|
uint8_t argName[16];
|
|
size_t sizeArgName = 0;
|
|
bool argNameProcess = false;
|
|
while (file.available()) {
|
|
uint8_t buf[64];
|
|
size_t length = file.read(buf, sizeof(buf));
|
|
size_t offset = 0;
|
|
|
|
if (argNameProcess) {
|
|
argEndPos = (uint8_t*) memchr(buf, '}', length);
|
|
|
|
if (argEndPos != nullptr) {
|
|
size_t fullSizeArgName = sizeArgName + (argEndPos - buf);
|
|
if (fullSizeArgName < sizeof(argName)) {
|
|
// copy full arg name
|
|
if (argEndPos - buf > 0) {
|
|
memcpy(argName + sizeArgName, buf, argEndPos - buf);
|
|
}
|
|
argName[fullSizeArgName] = '\0';
|
|
|
|
// send arg value
|
|
String argValue = this->templateCallback((const char*) argName);
|
|
if (argValue.length()) {
|
|
server.sendContent(argValue.c_str());
|
|
|
|
} else if (fullSizeArgName > 0) {
|
|
server.sendContent("{");
|
|
server.sendContent((const char*) argName);
|
|
server.sendContent("}");
|
|
}
|
|
|
|
offset = size_t(argEndPos - buf + 1);
|
|
sizeArgName = 0;
|
|
argNameProcess = false;
|
|
}
|
|
}
|
|
|
|
if (argNameProcess) {
|
|
server.sendContent("{");
|
|
|
|
if (sizeArgName > 0) {
|
|
argName[sizeArgName] = '\0';
|
|
server.sendContent((const char*) argName);
|
|
}
|
|
|
|
argNameProcess = false;
|
|
}
|
|
}
|
|
|
|
do {
|
|
uint8_t* currentBuf = buf + offset;
|
|
size_t currentLength = length - offset;
|
|
|
|
argStartPos = (uint8_t*) memchr(currentBuf, '{', currentLength);
|
|
|
|
// send all content
|
|
if (argStartPos == nullptr) {
|
|
if (currentLength > 0) {
|
|
server.sendContent((const char*) currentBuf, currentLength);
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
argEndPos = (uint8_t*) memchr(argStartPos, '}', length - (argStartPos - buf));
|
|
if (argEndPos != nullptr) {
|
|
sizeArgName = argEndPos - argStartPos - 1;
|
|
|
|
// send all content if arg len > space
|
|
if (sizeArgName >= sizeof(argName)) {
|
|
if (currentLength > 0) {
|
|
server.sendContent((const char*) currentBuf, currentLength);
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
// arg name
|
|
memcpy(argName, argStartPos + 1, sizeArgName);
|
|
argName[sizeArgName] = '\0';
|
|
|
|
// send arg value
|
|
String argValue = this->templateCallback((const char*) argName);
|
|
if (argValue.length()) {
|
|
// send content before var
|
|
if (argStartPos - buf > 0) {
|
|
server.sendContent((const char*) currentBuf, argStartPos - buf);
|
|
}
|
|
|
|
server.sendContent(argValue.c_str());
|
|
|
|
} else {
|
|
server.sendContent((const char*) currentBuf, argEndPos - currentBuf + 1);
|
|
}
|
|
|
|
offset = size_t(argEndPos - currentBuf + 1);
|
|
|
|
} else {
|
|
sizeArgName = length - size_t(argStartPos - currentBuf) - 1;
|
|
|
|
// send all content if arg len > space
|
|
if (sizeArgName >= sizeof(argName)) {
|
|
if (currentLength) {
|
|
server.sendContent((const char*) currentBuf, currentLength);
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
// send content before var
|
|
if (argStartPos - buf > 0) {
|
|
server.sendContent((const char*) currentBuf, argStartPos - buf);
|
|
}
|
|
|
|
// copy arg name chunk
|
|
if (sizeArgName > 0) {
|
|
memcpy(argName, argStartPos + 1, sizeArgName);
|
|
}
|
|
|
|
argNameProcess = true;
|
|
|
|
break;
|
|
}
|
|
} while(true);
|
|
}
|
|
|
|
file.close();
|
|
|
|
#ifdef ARDUINO_ARCH_ESP8266
|
|
server.chunkedResponseFinalize();
|
|
#else
|
|
server.sendContent(emptyString);
|
|
#endif
|
|
|
|
return true;
|
|
}
|
|
|
|
protected:
|
|
FS* fs = nullptr;
|
|
CanHandleCallback canHandleCallback;
|
|
BeforeSendCallback beforeSendCallback;
|
|
TemplateCallback templateCallback;
|
|
const char* uri = nullptr;
|
|
const char* path = nullptr;
|
|
const char* cacheHeader = nullptr;
|
|
}; |