168 lines
5.8 KiB
C++
168 lines
5.8 KiB
C++
#include "http_server.h"
|
|
#include "main.h"
|
|
#include "spi_master.h"
|
|
#include <ESPmDNS.h>
|
|
|
|
HTTPServer::HTTPServer(Player* p, Controller* c) {
|
|
_player = p;
|
|
_controller = c;
|
|
_server = new AsyncWebServer(80);
|
|
ws = new AsyncWebSocket("/ws");
|
|
_server->addHandler(ws);
|
|
ws->onEvent([&](AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventType type, void * arg, uint8_t *data, size_t len){this->_onEvent(server, client, type, arg, data, len);});
|
|
|
|
_server->on("/", HTTP_GET, [&](AsyncWebServerRequest* req) {
|
|
req->send(200, "text/html", (const char*)file_index_html_start);
|
|
});
|
|
_server->on("/upload", HTTP_POST, [](AsyncWebServerRequest* req) {
|
|
req->send(200);
|
|
}, [&](AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final) {
|
|
this->_handle_upload(request, filename, index, data, len, final);
|
|
});
|
|
_server->on("/_mapping.txt", HTTP_GET, [&](AsyncWebServerRequest* req) {
|
|
req->send(200, "text/plain", _controller->pm->create_mapping_txt());
|
|
});
|
|
_server->on("/player.json", HTTP_GET, [&](AsyncWebServerRequest* req) {
|
|
req->send(200, "application/json", _controller->player->json());
|
|
});
|
|
_server->on("/playlist_manager.json", HTTP_GET, [&](AsyncWebServerRequest* req) {
|
|
req->send(200, "application/json", _controller->pm->json());
|
|
});
|
|
_server->on("/controller.json", HTTP_GET, [&](AsyncWebServerRequest* req) {
|
|
req->send(200, "application/json", _controller->json());
|
|
});
|
|
_server->on("/position.json", HTTP_GET, [&](AsyncWebServerRequest* req) {
|
|
req->send(200, "application/json", _controller->player->position_json());
|
|
});
|
|
_server->on("/cmd", HTTP_POST, [&](AsyncWebServerRequest *req) {
|
|
if (req->hasParam("cmd", true)) {
|
|
_controller->queue_command(req->getParam("cmd", true)->value());
|
|
req->send(200);
|
|
} else {
|
|
req->send(400);
|
|
}
|
|
});
|
|
_server->begin();
|
|
MDNS.addService("http", "tcp", 80);
|
|
}
|
|
|
|
void HTTPServer::_handle_upload(AsyncWebServerRequest* request, String filename, size_t index, uint8_t* data, size_t len, bool final) {
|
|
// https://www.gnu.org/software/tar/manual/html_node/Standard.html
|
|
// https://www.mkssoftware.com/docs/man4/tar.4.asp
|
|
|
|
if (index == 0) { // Starting upload
|
|
_chunk = new uint8_t[512];
|
|
_chunk_length = 0;
|
|
_upload_position = 0;
|
|
_file_size = 0;
|
|
_file_size_done = 0;
|
|
_need_header = true;
|
|
}
|
|
|
|
uint32_t upload_offset = 0;
|
|
while (upload_offset < len) {
|
|
// Load a chunk
|
|
if (_chunk_length < 512 && len > upload_offset) {
|
|
uint16_t needed = 512 - _chunk_length;
|
|
if (needed > len - upload_offset) needed = len - upload_offset;
|
|
memcpy(_chunk + _chunk_length, data + upload_offset, needed);
|
|
_chunk_length += needed;
|
|
upload_offset += needed;
|
|
_upload_position += needed;
|
|
|
|
if (_chunk_length == 512) {
|
|
// Process chunk
|
|
DEBUG(".");
|
|
if (_need_header) {
|
|
if (_chunk[257]=='u'&&_chunk[258]=='s'&&_chunk[259]=='t'&&_chunk[260]=='a'&&_chunk[261]=='r') {
|
|
DEBUG("It is a valid header, starting at 0x%X!\n", _upload_position-512);
|
|
char filename[200];
|
|
strncpy(filename, (char*)_chunk, 100);
|
|
DEBUG("filename: %s\n", filename);
|
|
_file_size = 0;
|
|
_file_size_done = 0;
|
|
for (int i=0; i<11; i++) {
|
|
//Serial.print(_header_buffer[124 + i]);
|
|
_file_size = (_file_size<<3) + (_chunk[124 + i] - '0');
|
|
}
|
|
DEBUG("filesize: %d\n", _file_size);
|
|
uint8_t type = _chunk[156] - '0';
|
|
if (type==0) {
|
|
String path = "/";
|
|
path += filename;
|
|
DEBUG("Opening file %s\n", path.c_str());
|
|
uint8_t state = SPIMaster::state;
|
|
SPIMaster::disable();
|
|
SPIMaster::select_sd();
|
|
// Better safe than sorry. ;-)
|
|
_upload_file.close();
|
|
_upload_file = SD.open(path, "w");
|
|
SPIMaster::set_state(state);
|
|
} else if (type==5) {
|
|
String dirname = "/";
|
|
dirname += filename;
|
|
dirname.remove(dirname.length()-1);
|
|
uint8_t state = SPIMaster::state;
|
|
SPIMaster::disable();
|
|
SPIMaster::select_sd();
|
|
bool res = SD.mkdir(dirname);
|
|
SPIMaster::set_state(state);
|
|
DEBUG("Creating folder '%s' returned %d.\n", dirname.c_str(), res);
|
|
} else {
|
|
ERROR("Unknown file type %d\n", type);
|
|
}
|
|
_need_header = (type==5 || _file_size==0); // No chunks needed for directories.
|
|
} else {
|
|
bool byte_found = false;
|
|
for (int i=0; i<512; i++) byte_found = byte_found || _chunk[i]>0;
|
|
if (!byte_found) {
|
|
DEBUG("Empty chunk while looking for header -> ignoring.\n");
|
|
} else {
|
|
ERROR("Invalid tar header: %c %c %c %c %c. Looking at header start offset 0x%X.\n", _chunk[257], _chunk[258], _chunk[259], _chunk[260], _chunk[261], _upload_position-512);
|
|
}
|
|
}
|
|
} else {
|
|
uint32_t bytes_to_write = _file_size - _file_size_done;
|
|
if (bytes_to_write > 512) bytes_to_write=512;
|
|
uint8_t state = SPIMaster::state;
|
|
SPIMaster::disable();
|
|
SPIMaster::select_sd();
|
|
_upload_file.write(_chunk, bytes_to_write);
|
|
_file_size_done += bytes_to_write;
|
|
if (_file_size_done >= _file_size) {
|
|
_upload_file.close();
|
|
_need_header = true;
|
|
}
|
|
SPIMaster::set_state(state);
|
|
}
|
|
|
|
_chunk_length = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (final == true) {
|
|
uint8_t state = SPIMaster::state;
|
|
SPIMaster::disable();
|
|
SPIMaster::select_sd();
|
|
_upload_file.close();
|
|
SPIMaster::set_state(state);
|
|
delete _chunk;
|
|
_controller->update_playlist_manager();
|
|
return;
|
|
}
|
|
}
|
|
|
|
void HTTPServer::_onEvent(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventType type, void * arg, uint8_t *data, size_t len) {
|
|
if (type==WS_EVT_CONNECT) {
|
|
_controller->inform_new_client(client);
|
|
} else if (type==WS_EVT_DATA) {
|
|
AwsFrameInfo* info = (AwsFrameInfo*) arg;
|
|
if (info->final && info->index==0 && info->len==len && info->opcode==WS_TEXT) {
|
|
data[len]='\0';
|
|
DEBUG("Received ws message: %s\n", (char*)data);
|
|
_controller->queue_command((char*)data);
|
|
}
|
|
}
|
|
}
|