Switched to native ESP32 webserver

- new status API
- webdav without crashing
This commit is contained in:
Martin Bauer
2020-06-20 19:28:34 +02:00
parent e929c8d3d4
commit fc469f47a6
8 changed files with 385 additions and 111 deletions

View File

@@ -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

View 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;
}

View 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);

View 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);
}
}

View File

@@ -0,0 +1,4 @@
#include "Dtypes.h"
#include "EspHttp.h"
void webdavHandler(httpd_req_t *req);

View File

@@ -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
{