Switched to native ESP32 webserver
- new status API - webdav without crashing
This commit is contained in:
@@ -74,6 +74,16 @@ namespace portablefs
|
||||
{
|
||||
return SPIFFS.mkdir(name);
|
||||
}
|
||||
|
||||
inline size_t usedBytes()
|
||||
{
|
||||
return SPIFFS.usedBytes();
|
||||
}
|
||||
|
||||
inline size_t totalBytes()
|
||||
{
|
||||
return SPIFFS.totalBytes();
|
||||
}
|
||||
|
||||
} // namespace portablefs
|
||||
|
||||
|
||||
27
firmware/lib/esphttp/EspHttp.cpp
Normal file
27
firmware/lib/esphttp/EspHttp.cpp
Normal file
@@ -0,0 +1,27 @@
|
||||
#include "EspHttp.h"
|
||||
|
||||
esp_err_t rawCallback(httpd_req_t *req)
|
||||
{
|
||||
EspHttp::Callback *userCb = (EspHttp::Callback *)(req->user_ctx);
|
||||
(*userCb)(req);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
int getUrlQueryParameter(httpd_req_t *req, const char *name, int defaultValue)
|
||||
{
|
||||
size_t bufferLength = httpd_req_get_url_query_len(req) + 1;
|
||||
int result = defaultValue;
|
||||
if (bufferLength > 1)
|
||||
{
|
||||
char* buf = (char*)malloc(bufferLength);
|
||||
if (httpd_req_get_url_query_str(req, buf, bufferLength) == ESP_OK)
|
||||
{
|
||||
char param[32];
|
||||
/* Get value of expected key from query string */
|
||||
if (httpd_query_key_value(buf, name, param, sizeof(param)) == ESP_OK)
|
||||
result = atoi(param);
|
||||
}
|
||||
free(buf);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
71
firmware/lib/esphttp/EspHttp.h
Normal file
71
firmware/lib/esphttp/EspHttp.h
Normal file
@@ -0,0 +1,71 @@
|
||||
#pragma once
|
||||
|
||||
#include <esp_http_server.h>
|
||||
#include <functional>
|
||||
#include "Dtypes.h"
|
||||
|
||||
constexpr int MAX_URI_HANDLERS = 10;
|
||||
|
||||
esp_err_t rawCallback(httpd_req_t *req);
|
||||
|
||||
class EspHttp
|
||||
{
|
||||
public:
|
||||
using Callback = std::function<void(httpd_req_t *)>;
|
||||
|
||||
EspHttp(int port = 80) : server_(0), nextFreeHandler_(0)
|
||||
{
|
||||
config_ = HTTPD_DEFAULT_CONFIG();
|
||||
config_.server_port = port;
|
||||
config_.max_uri_handlers = MAX_URI_HANDLERS;
|
||||
config_.uri_match_fn = httpd_uri_match_wildcard;
|
||||
}
|
||||
~EspHttp()
|
||||
{
|
||||
stop();
|
||||
}
|
||||
|
||||
void start()
|
||||
{
|
||||
if (httpd_start(&server_, &config_) != ESP_OK)
|
||||
assert_msg(false, "HTTPD server didn't start");
|
||||
}
|
||||
|
||||
void stop()
|
||||
{
|
||||
if (server_)
|
||||
httpd_stop(server_);
|
||||
server_ = 0;
|
||||
}
|
||||
|
||||
void on(const char *uri, httpd_method_t method, Callback cb)
|
||||
{
|
||||
if (nextFreeHandler_ == MAX_URI_HANDLERS)
|
||||
{
|
||||
assert_msg(nextFreeHandler_ < MAX_URI_HANDLERS, "Too many handlers - increase MAX_URI_HANDLERS");
|
||||
return;
|
||||
}
|
||||
handlerCallbacks_[nextFreeHandler_] = cb;
|
||||
|
||||
httpd_uri_t uri_endpoint_cfg = {
|
||||
.uri = uri,
|
||||
.method = method,
|
||||
.handler = rawCallback,
|
||||
.user_ctx = (void *)(&handlerCallbacks_[nextFreeHandler_])};
|
||||
if (httpd_register_uri_handler(server_, &uri_endpoint_cfg) != ESP_OK)
|
||||
Serial.println("Failed to register url handler");
|
||||
++nextFreeHandler_;
|
||||
}
|
||||
|
||||
void write(const char *data, ssize_t length);
|
||||
|
||||
private:
|
||||
httpd_config_t config_;
|
||||
httpd_handle_t server_;
|
||||
int nextFreeHandler_;
|
||||
Callback handlerCallbacks_[MAX_URI_HANDLERS];
|
||||
};
|
||||
|
||||
esp_err_t rawCallback(httpd_req_t *req);
|
||||
|
||||
int getUrlQueryParameter(httpd_req_t *req, const char *name, int defaultValue);
|
||||
137
firmware/lib/esphttp/WebDAV.cpp
Normal file
137
firmware/lib/esphttp/WebDAV.cpp
Normal file
@@ -0,0 +1,137 @@
|
||||
#include "FilesystemAbstraction.h"
|
||||
#include "WebDAV.h"
|
||||
|
||||
namespace webdav_constants
|
||||
{
|
||||
constexpr const char *MULTISTATUS_START = "<?xml version=\"1.0\" ?><D:multistatus xmlns:D=\"DAV:\">";
|
||||
constexpr const char *MULTISTATUS_END = "</D:multistatus>";
|
||||
constexpr const char *RESPONSE_START = "<D:response>";
|
||||
constexpr const char *RESPONSE_END = "</D:response>\n";
|
||||
constexpr const char *HREF_START = "<D:href>";
|
||||
constexpr const char *HREF_END = "</D:href>";
|
||||
constexpr const char *PROPSTAT_START = "<D:propstat>";
|
||||
constexpr const char *PROPSTAT_END = "</D:propstat>";
|
||||
constexpr const char *PROP_START = "<D:prop>";
|
||||
constexpr const char *PROP_END = "</D:prop>";
|
||||
constexpr const char *RESOURCETYPE_START = "<D:resourcetype>";
|
||||
constexpr const char *RESOURCETYPE_END = "</D:resourcetype>";
|
||||
constexpr const char *RESOURCE_COLLECTION = "<D:collection/>";
|
||||
constexpr const char *HTTP_204_NO_CONTENT = "HTTP/1.1 204 No Content";
|
||||
|
||||
constexpr const char *CONTENTLEN_START = "<D:getcontentlength>";
|
||||
constexpr const char *CONTENTLEN_END = "</D:getcontentlength>";
|
||||
constexpr const char *CREATEDATE_START = "<D:creationdate>";
|
||||
constexpr const char *CREATEDATE_END = "</D:creationdate>";
|
||||
constexpr const char *MODDATE_START = "<D:getlastmodified>";
|
||||
constexpr const char *MODDATE_END = "</D:getlastmodified>";
|
||||
constexpr const char *STATUS_OK = "<D:status>HTTP/1.1 200 OK</D:status>";
|
||||
constexpr const char *USED_BYTES_START = "<D:quota-used-bytes>";
|
||||
constexpr const char *USED_BYTES_END = "</D:quota-used-bytes>";
|
||||
} // namespace webdav_constants
|
||||
|
||||
size_t webdavFileListingSpiffs(char *buffer, size_t maxLength,
|
||||
const char *spiffsPath)
|
||||
{
|
||||
using namespace webdav_constants;
|
||||
|
||||
size_t bytesWritten = 0;
|
||||
|
||||
auto toBuffer = [&buffer, &bytesWritten](const char *text) {
|
||||
auto len = strlen(text);
|
||||
memcpy(buffer, text, len);
|
||||
buffer += len;
|
||||
bytesWritten += len;
|
||||
};
|
||||
|
||||
// crude upper bound on how much space a file entry + footer needs
|
||||
constexpr size_t sizePerFile = 512;
|
||||
assert(maxLength >= sizePerFile);
|
||||
|
||||
int fileIdx = 0;
|
||||
|
||||
auto dir = portablefs::openDir(spiffsPath);
|
||||
|
||||
toBuffer(MULTISTATUS_START);
|
||||
|
||||
bool incomplete = false;
|
||||
while (dir.next())
|
||||
{
|
||||
const auto freeSpace = maxLength - bytesWritten;
|
||||
if (freeSpace < sizePerFile)
|
||||
{
|
||||
incomplete = true;
|
||||
break;
|
||||
}
|
||||
fileIdx += 1;
|
||||
toBuffer(RESPONSE_START);
|
||||
toBuffer(HREF_START);
|
||||
const auto fileName = dir.fileName();
|
||||
const auto fileNameWithoutDir = fileName.substring(fileName.lastIndexOf("/") + 1);
|
||||
toBuffer((fileNameWithoutDir + ".st").c_str());
|
||||
toBuffer(HREF_END);
|
||||
toBuffer(PROPSTAT_START);
|
||||
toBuffer(PROP_START);
|
||||
if (dir.isDirectory())
|
||||
{
|
||||
toBuffer(RESOURCETYPE_START);
|
||||
toBuffer(RESOURCE_COLLECTION);
|
||||
toBuffer(RESOURCETYPE_END);
|
||||
}
|
||||
else
|
||||
{
|
||||
toBuffer(CONTENTLEN_START);
|
||||
String fileSizeStr(dir.fileSize());
|
||||
toBuffer(fileSizeStr.c_str());
|
||||
toBuffer(CONTENTLEN_END);
|
||||
}
|
||||
|
||||
toBuffer(PROP_END);
|
||||
toBuffer(STATUS_OK);
|
||||
toBuffer(PROPSTAT_END);
|
||||
toBuffer(webdav_constants::RESPONSE_END);
|
||||
}
|
||||
toBuffer(MULTISTATUS_END);
|
||||
|
||||
if (incomplete)
|
||||
Serial.println("WebDAV listing response is incomplete, because buffer was too small");
|
||||
return bytesWritten;
|
||||
}
|
||||
|
||||
String uriToFileName(const String &uriStr)
|
||||
{
|
||||
String filename;
|
||||
if (uriStr.endsWith(".st"))
|
||||
filename = uriStr.substring(0, uriStr.length() - strlen(".st"));
|
||||
|
||||
filename = "/dat/" + filename;
|
||||
return filename;
|
||||
}
|
||||
|
||||
void webdavHandler(httpd_req_t *req)
|
||||
{
|
||||
String uri = String(req->uri).substring(strlen("/webdav/"));
|
||||
constexpr size_t WEBDAV_BUFF_LEN = 1024 * 256;
|
||||
static char *webdavBuffer = (char *)heap_caps_malloc(WEBDAV_BUFF_LEN, MALLOC_CAP_SPIRAM);
|
||||
switch (req->method)
|
||||
{
|
||||
case HTTP_GET:
|
||||
case HTTP_PROPFIND:
|
||||
{
|
||||
size_t bytesWritten = webdavFileListingSpiffs(webdavBuffer, WEBDAV_BUFF_LEN, "/dat");
|
||||
httpd_resp_send(req, webdavBuffer, bytesWritten);
|
||||
break;
|
||||
}
|
||||
case HTTP_DELETE:
|
||||
{
|
||||
httpd_resp_set_hdr(req, "Content-Type", "text/plain");
|
||||
|
||||
if (portablefs::remove(uriToFileName(uri).c_str()))
|
||||
httpd_resp_send(req, "Deleted file", -1);
|
||||
else
|
||||
httpd_resp_send_404(req);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
httpd_resp_send_404(req);
|
||||
}
|
||||
}
|
||||
4
firmware/lib/esphttp/WebDAV.h
Normal file
4
firmware/lib/esphttp/WebDAV.h
Normal file
@@ -0,0 +1,4 @@
|
||||
#include "Dtypes.h"
|
||||
#include "EspHttp.h"
|
||||
|
||||
void webdavHandler(httpd_req_t *req);
|
||||
@@ -39,6 +39,10 @@ public:
|
||||
otherChunk->init(0, 0);
|
||||
}
|
||||
|
||||
uint32_t numMeasurements() const {
|
||||
return currentChunk->getStartIndex() + currentChunk->numMeasurements();
|
||||
}
|
||||
|
||||
template<typename Encoder_T>
|
||||
void serialize(Encoder_T & encoder, uint32_t startIdx) const
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user