/* HTTP Server setup and handler functions */ #include #include #include "WebServer.h" #include "Config.h" #include "Fader.h" WebServer::WebServer(void) : m_cr(Config::instance().getCRPassword()), m_fader(NULL) { m_server = new httpsserver::HTTPServer(); } bool WebServer::serveFile(String filename, httpsserver::HTTPResponse *res) { uint8_t buf[1024]; File f = SPIFFS.open(filename.c_str(), "r"); if(!f) { return false; } res->setHeader("Content-Length", httpsserver::intToString(f.size())); size_t nread = 1; while(nread > 0) { nread = f.readBytes(reinterpret_cast(buf), 1024); if(nread <= 0) { break; } res->write(buf, nread); } f.close(); return true; } void WebServer::handleRoot(httpsserver::HTTPRequest *req, httpsserver::HTTPResponse *res) { serveFile("/htdocs/index.html", res); res->setHeader("Content-Type", "text/html"); } static void error400(httpsserver::HTTPResponse *res, const std::string &reason) { res->println("Error 400: Bad Request"); res->println(reason.c_str()); res->setStatusCode(400); } void WebServer::handleColor(httpsserver::HTTPRequest *req, httpsserver::HTTPResponse *res) { res->setHeader("Content-Type", "text/plain"); httpsserver::ResourceParameters * params = req->getParams(); if(!params->isRequestParameterSet("color")) { error400(res, "Parameter 'color' not set."); return; } std::string colorstr = params->getRequestParameter("color"); if(colorstr.length() != 8) { error400(res, "Wrong length of color string (expected 8 hex digits)."); return; } uint32_t color = 0; for(size_t i = 0; i < 8; i++) { color <<= 4; char c = colorstr[i]; if(c >= 'A' && c <= 'F') { color |= c - 'A' + 10; } else if(c >= 'a' && c <= 'f') { color |= c - 'a' + 10; } else if(c >= '0' && c <= '9') { color |= c - '0'; } else { error400(res, "Invalid character in color string (allowed: 0-9, a-f, A-F)"); return; } } uint8_t r = (color & 0xFF000000) >> 24; uint8_t g = (color & 0x00FF0000) >> 16; uint8_t b = (color & 0x0000FF00) >> 8; uint8_t w = (color & 0x000000FF) >> 0; res->print("Color changed to r:"); res->print(r); res->print(" g:"); res->print(g); res->print(" b:"); res->print(b); res->print(" w:"); res->println(w); WebServer::instance().m_fader->fade_color({r,g,b,w}); } void WebServer::handleUpdate(httpsserver::HTTPRequest *req, httpsserver::HTTPResponse *res) { res->setHeader("Content-Type", "text/plain"); httpsserver::ResourceParameters * params = req->getParams(); if(!params->isRequestParameterSet("content")) { error400(res, "Send update image as 'content'."); } res->print("Content length: "); res->println(req->getContentLength()); res->println("Content:"); res->println("--------------------------------------------------------------------------------"); uint8_t buf[128]; while(!req->requestComplete()) { size_t bytes_processed = req->readBytes(buf, 128); res->write(buf, bytes_processed); } res->println("--------------------------------------------------------------------------------"); std::vector d; d.push_back(1); d.push_back(2); d.push_back(3); uint8_t sha256sum[32]; mbedtls_sha256_ret(d.data(), d.size(), sha256sum, 0); for(size_t i = 0; i < 32; i++) { static const char *conv = "0123456789abcdef"; uint8_t b = sha256sum[i]; res->print(conv[(b >> 4)]); res->print(conv[(b &0x0F)]); } res->println(); } void WebServer::handleChallenge(httpsserver::HTTPRequest *req, httpsserver::HTTPResponse *res) { res->setHeader("Content-Type", "application/json"); std::ostringstream responseBuilder; responseBuilder << "{ \"nonce\": " << WebServer::instance().m_cr.nonce() << "}"; res->println(responseBuilder.str().c_str()); } void WebServer::handleAuthTest(httpsserver::HTTPRequest *req, httpsserver::HTTPResponse *res) { res->setHeader("Content-Type", "text/plain"); httpsserver::ResourceParameters * params = req->getParams(); if(!params->isRequestParameterSet("response")) { error400(res, "Parameter 'response' not set."); return; } std::string response = params->getRequestParameter("response"); bool result = WebServer::instance().m_cr.verify(response); if(result) { res->println("Authentication test successful!"); } else { res->println("Authentication test failed (invalid response)"); } } void WebServer::handleStatic(httpsserver::HTTPRequest *req, httpsserver::HTTPResponse *res) { std::string filename = "/htdocs" + req->getRequestString(); if(!serveFile(filename.c_str(), res)) { req->discardRequestBody(); res->setStatusCode(404); res->setHeader("Content-Type", "text/plain"); res->println("Error 404: Not found"); } } void WebServer::serverTask(void *arg) { httpsserver::ResourceNode *nodeRoot = new httpsserver::ResourceNode("/", "GET", WebServer::handleRoot); httpsserver::ResourceNode *nodeColor = new httpsserver::ResourceNode("/color", "GET", WebServer::handleColor); httpsserver::ResourceNode *nodeUpdate = new httpsserver::ResourceNode("/update", "POST", WebServer::handleUpdate); httpsserver::ResourceNode *nodeChallenge = new httpsserver::ResourceNode("/challenge", "GET", WebServer::handleChallenge); httpsserver::ResourceNode *nodeAuthTest = new httpsserver::ResourceNode("/authtest", "GET", WebServer::handleAuthTest); // handle all remaining requests by trying to serve static files. If no file is found, 404 is generated. httpsserver::ResourceNode *nodeStatic = new httpsserver::ResourceNode("", "GET", WebServer::handleStatic); WebServer &server = WebServer::instance(); server.m_server->registerNode(nodeRoot); server.m_server->registerNode(nodeColor); server.m_server->registerNode(nodeUpdate); server.m_server->registerNode(nodeChallenge); server.m_server->registerNode(nodeAuthTest); server.m_server->setDefaultNode(nodeStatic); Serial.println("[server] Starting HTTP Server..."); server.m_server->start(); if (server.m_server->isRunning()) { Serial.println("[server] Server ready."); while (true) { server.m_server->loop(); } } Serial.println("[server] Server died?! This should never happen."); } bool WebServer::start(void) { xTaskCreate( WebServer::serverTask, /* Task function. */ "HTTP Server Task", /* name of task. */ 6144, /* Stack size of task */ NULL, /* parameter of the task */ 1, /* priority of the task */ NULL); /* Task handle to keep track of created task */ return true; }