You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
300 lines
8.6 KiB
300 lines
8.6 KiB
#include "WebServer.h" |
|
|
|
#include "DebugLog.h" |
|
#include "DataLogger.h" |
|
#include "Info.h" |
|
#include "utils.h" |
|
|
|
#include <Arduino.h> |
|
#include <SPIFFS.h> |
|
#include <string> |
|
#include <cstring> |
|
|
|
#include "HTTPSServer.hpp" |
|
#include "SSLCert.hpp" |
|
#include "HTTPRequest.hpp" |
|
#include "HTTPResponse.hpp" |
|
|
|
// generated certificate data |
|
#include "cert.h" |
|
#include "private_key.h" |
|
|
|
/** Check if we have multiple cores */ |
|
#if CONFIG_FREERTOS_UNICORE |
|
#define WEBSERVER_RUNNING_CORE 0 |
|
#else |
|
#define WEBSERVER_RUNNING_CORE 1 |
|
#endif |
|
|
|
using namespace httpsserver; |
|
|
|
detail::WebServer WebServer; |
|
|
|
namespace detail |
|
{ |
|
// Create an SSL certificate object from the files included above |
|
SSLCert cert = SSLCert( |
|
crt_DER, crt_DER_len, |
|
private_key_DER, private_key_DER_len |
|
); |
|
|
|
HTTPSServer httpServer = HTTPSServer(&cert); |
|
|
|
WebServer::WebServer() |
|
{ |
|
} |
|
|
|
WebServer::~WebServer() |
|
{ |
|
} |
|
|
|
void WebServer::begin() |
|
{ |
|
::DebugLog.println("Creating server task... "); |
|
xTaskCreatePinnedToCore(WebServer::ServerTask_, "webserver", 6144, nullptr, 1, nullptr, WEBSERVER_RUNNING_CORE); |
|
} |
|
|
|
void WebServer::setStatus(const Status& status) |
|
{ |
|
status_ = status; |
|
} |
|
|
|
void WebServer::ServerTask_(void* params) |
|
{ |
|
// Main HTML page |
|
ResourceNode* nodeRoot = new ResourceNode("/", "GET", &WebServer::HandleIndex_); |
|
ResourceNode* nodeIndex = new ResourceNode("/index.html", "GET", &WebServer::HandleIndex_); |
|
httpServer.registerNode(nodeRoot); |
|
httpServer.registerNode(nodeIndex); |
|
|
|
// API |
|
ResourceNode* nodeGetStatus = new ResourceNode("/api/status", "GET", &WebServer::HandleGetStatus_); |
|
ResourceNode* nodePostInfo = new ResourceNode("/api/info", "POST", &WebServer::HandlePostInfo_); |
|
ResourceNode* nodeGetLogList = new ResourceNode("/api/log/list", "GET", &WebServer::HandleLogList_); |
|
ResourceNode* nodeGetLog = new ResourceNode("/api/log/*", "GET", &WebServer::HandleLog_); |
|
httpServer.registerNode(nodeGetStatus); |
|
httpServer.registerNode(nodePostInfo); |
|
httpServer.registerNode(nodeGetLogList); |
|
httpServer.registerNode(nodeGetLog); |
|
|
|
// Default node (either a static file, if found, or a 404 error) |
|
ResourceNode* nodeDefault = new ResourceNode("", "GET", &WebServer::HandleDefault_); |
|
httpServer.setDefaultNode(nodeDefault); |
|
|
|
::DebugLog.println("Starting server... "); |
|
httpServer.start(); |
|
if (httpServer.isRunning()) { |
|
::DebugLog.println("Server ready."); |
|
|
|
while(true) { |
|
httpServer.loop(); |
|
delay(1); |
|
} |
|
} |
|
} |
|
|
|
void WebServer::HandleIndex_(HTTPRequest * request, HTTPResponse * response) |
|
{ |
|
// Status code is 200 OK by default. |
|
response->setHeader("Content-Type", "text/html"); |
|
|
|
File file = SPIFFS.open("/www/index.html"); |
|
SendContent_(file, response); |
|
file.close(); |
|
} |
|
|
|
void WebServer::HandleGetStatus_(httpsserver::HTTPRequest * request, httpsserver::HTTPResponse * response) |
|
{ |
|
const Status& status = ::WebServer.status_; |
|
|
|
int v = status.batteryVoltage; |
|
int c = status.batteryOutputCurrent; |
|
int s = (int)(status.speed * 1000.0f + 0.5f); |
|
int temp = status.temperature; |
|
int alt = status.altitude; |
|
|
|
int td = status.tripDistance; |
|
int ttt = status.tripTotalTime; |
|
int tmt = status.tripMovingTime; |
|
int tae = status.tripAscendingElevation / 100; // convert mm to dm |
|
int tme = status.tripMotorEnergy / 360; // convert Joules to dWh (tenth of Wh) |
|
|
|
float latitude = -1000.0f; |
|
float longitude = -1000.0f; |
|
char realtime[64] = {0}; |
|
|
|
const char* logFileName = DataLogger::get().currentLogFileName(); |
|
if(String(logFileName).startsWith("/log/")) logFileName += 5; |
|
|
|
int totalSize = (int)(SPIFFS.totalBytes() / 1000); |
|
int usedSize = (int)(SPIFFS.usedBytes() / 1000); |
|
|
|
char json[256]; |
|
sprintf(json, "{\"v\":%d,\"c\":%d,\"s\":%d,\"td\":%d,\"ttt\":%d,\"tmt\":%d,\"tae\":%d,\"tme\":%d,\"temp\":%d,\"alt\":%d,\"log\":\"%s\",\"tot\":%d,\"used\":%d,\"lat\":%.5f,\"lng\":%.5f,\"d\":\"%s\"}", v, c, s, td, ttt, tmt, tae, tme, temp, alt, logFileName, totalSize, usedSize, latitude, longitude, realtime); |
|
|
|
response->setHeader("Content-Type", "text/json"); |
|
response->print(json); |
|
} |
|
|
|
void WebServer::HandlePostInfo_(httpsserver::HTTPRequest * request, httpsserver::HTTPResponse * response) |
|
{ |
|
char body[128]; // in current implementation, we expect the whole body to fit here |
|
size_t bodySize = request->readChars(body, sizeof(body) - 1); |
|
body[bodySize] = 0; |
|
|
|
request->discardRequestBody(); |
|
|
|
//::DebugLog.println(body.c_str()); |
|
|
|
Info receivedInfo; |
|
|
|
char* nextParam = body; |
|
while (true) |
|
{ |
|
char* sepPos = strstr(nextParam, "&"); |
|
if(sepPos == nullptr) sepPos = &body[bodySize]; |
|
if(nextParam == sepPos) |
|
break; // should happen only if body is an empty string or ends with an "&" character |
|
|
|
*sepPos = 0; // split the string in-place, this overrides the separating character which is fine |
|
//::DebugLog.println(nextParam); |
|
|
|
char* eqPos = strstr(nextParam, "="); |
|
if(eqPos != nullptr && eqPos != nextParam && *(eqPos+1) != 0) |
|
{ |
|
*eqPos = 0; // split the string in-place, overriding the equal sign |
|
|
|
char* paramName = nextParam; |
|
char* paramValue = eqPos + 1; |
|
utils::replaceString(paramValue, "%3A", ":"); |
|
//::DebugLog.print(paramName); ::DebugLog.print(" = "); ::DebugLog.println(paramValue); |
|
|
|
if(strcmp(paramName, "lat") == 0) |
|
{ |
|
char *ending = nullptr; |
|
receivedInfo.latitude = strtof(paramValue, &ending); |
|
if (*ending != 0) |
|
receivedInfo.latitude = -1000.0f; |
|
|
|
} |
|
else if(strcmp(paramName, "lng") == 0) |
|
{ |
|
char *ending = nullptr; |
|
receivedInfo.longitude = strtof(paramValue, &ending); |
|
if (*ending != 0) |
|
receivedInfo.longitude = -1000.0f; |
|
|
|
} |
|
else if(strcmp(paramName, "alt") == 0) |
|
{ |
|
char *ending = nullptr; |
|
receivedInfo.gpsAltitude = strtof(paramValue, &ending); |
|
if (*ending != 0) |
|
receivedInfo.gpsAltitude = -1000.0f; |
|
|
|
} |
|
else if(strcmp(paramName, "time") == 0) |
|
{ |
|
strncpy(receivedInfo.realtime, paramValue, sizeof(receivedInfo.realtime)); |
|
} |
|
} |
|
|
|
if(sepPos == &body[bodySize]) |
|
break; |
|
nextParam = sepPos + 1; |
|
} |
|
|
|
if(::WebServer.infoReceived_ != nullptr) |
|
{ |
|
::WebServer.infoReceived_(receivedInfo); |
|
} |
|
} |
|
|
|
void WebServer::HandleLogList_(HTTPRequest * request, HTTPResponse * response) |
|
{ |
|
String json; |
|
|
|
json = "{\"files\":["; |
|
|
|
auto logFolder = SPIFFS.open("/log"); |
|
auto file = logFolder.openNextFile(); |
|
bool first = true; |
|
while(file) |
|
{ |
|
if(!first) json += ","; |
|
json += "{\"n\":\"/api/log/"; |
|
//::DebugLog.println(file.name()); |
|
json += file.name(); |
|
json += "\",\"s\":"; |
|
json += file.size(); |
|
json += "}"; |
|
|
|
first = false; |
|
file = logFolder.openNextFile(); |
|
} |
|
|
|
json += "]}"; |
|
|
|
response->setHeader("Content-Type", "text/json"); |
|
response->print(json.c_str()); |
|
} |
|
|
|
void WebServer::HandleLog_(HTTPRequest * request, HTTPResponse * response) |
|
{ |
|
auto* params = request->getParams(); |
|
std::string path = params->getPathParameter(0); |
|
std::string fullPath = std::string("/log/") + path; |
|
File file = SPIFFS.open(fullPath.c_str()); |
|
SendContent_(file, response); |
|
file.close(); |
|
} |
|
|
|
void WebServer::HandleDefault_(HTTPRequest * request, HTTPResponse * response) |
|
{ |
|
// Discard request body, if we received any |
|
// We do this, as this is the default node and may also server POST/PUT requests |
|
request->discardRequestBody(); |
|
|
|
std::string filePath = "/www"; |
|
std::string fullURL = request->getRequestString(); |
|
filePath = filePath + fullURL.substr(0, fullURL.find("?")); |
|
//::DebugLog.println(filePath.c_str()); |
|
File file = SPIFFS.open(filePath.c_str()); |
|
|
|
if(!file || file.isDirectory()) |
|
{ |
|
// We've not found any matching file, so this is a 404 error |
|
|
|
response->setStatusCode(404); |
|
response->setStatusText("Not Found"); |
|
|
|
response->setHeader("Content-Type", "text/html"); |
|
|
|
response->println("<!DOCTYPE html>"); |
|
response->println("<html>"); |
|
response->println("<head><title>Not Found</title></head>"); |
|
response->println("<body><h1>404 Not Found</h1><p>The requested resource was not found on this server.</p></body>"); |
|
response->println("</html>"); |
|
} |
|
else |
|
{ |
|
// We've found a file, send the content |
|
|
|
response->setHeader("Cache-Control", "max-age=5184000"); |
|
|
|
SendContent_(file, response); |
|
} |
|
|
|
file.close(); |
|
} |
|
|
|
void WebServer::SendContent_(Stream& stream, httpsserver::HTTPResponse * response) |
|
{ |
|
uint8_t buffer[128]; |
|
while(stream.available()) |
|
{ |
|
size_t numBytes = stream.readBytes(buffer, sizeof(buffer)); |
|
response->write(buffer, numBytes); |
|
} |
|
} |
|
}
|
|
|