diff --git a/components/spotify/cspot/bell/include/BaseHTTPServer.h b/components/spotify/cspot/bell/include/BaseHTTPServer.h index 6189a217..15c283c7 100644 --- a/components/spotify/cspot/bell/include/BaseHTTPServer.h +++ b/components/spotify/cspot/bell/include/BaseHTTPServer.h @@ -53,6 +53,7 @@ struct HTTPResponse { bool useGzip = false; std::string body; std::string contentType; + std::vector extraHeaders = std::vector(); std::unique_ptr responseReader; }; diff --git a/components/spotify/cspot/bell/include/HTTPServer.h b/components/spotify/cspot/bell/include/HTTPServer.h index 9285d372..28072f52 100644 --- a/components/spotify/cspot/bell/include/HTTPServer.h +++ b/components/spotify/cspot/bell/include/HTTPServer.h @@ -62,6 +62,7 @@ namespace bell void registerHandler(RequestType requestType, const std::string &, httpHandler); void respond(const HTTPResponse &); + void redirectTo(const std::string&, int connectionFd); void publishEvent(std::string eventName, std::string eventData); void closeConnection(int connection); void listen(); diff --git a/components/spotify/cspot/bell/include/Task.h b/components/spotify/cspot/bell/include/Task.h index 8d478d41..f08ea169 100644 --- a/components/spotify/cspot/bell/include/Task.h +++ b/components/spotify/cspot/bell/include/Task.h @@ -3,6 +3,7 @@ #ifdef ESP_PLATFORM #include +#include #include #include #include diff --git a/components/spotify/cspot/bell/include/platform/WrappedSemaphore.h b/components/spotify/cspot/bell/include/platform/WrappedSemaphore.h index 74b41c82..ef9106a6 100644 --- a/components/spotify/cspot/bell/include/platform/WrappedSemaphore.h +++ b/components/spotify/cspot/bell/include/platform/WrappedSemaphore.h @@ -7,6 +7,7 @@ #elif __APPLE__ #include #else +#include #include #endif diff --git a/components/spotify/cspot/bell/src/HTTPServer.cpp b/components/spotify/cspot/bell/src/HTTPServer.cpp index c4d5ad4f..82ee3808 100644 --- a/components/spotify/cspot/bell/src/HTTPServer.cpp +++ b/components/spotify/cspot/bell/src/HTTPServer.cpp @@ -1,51 +1,37 @@ #include "HTTPServer.h" -bell::HTTPServer::HTTPServer(int serverPort) -{ - this->serverPort = serverPort; -} +bell::HTTPServer::HTTPServer(int serverPort) { this->serverPort = serverPort; } -unsigned char bell::HTTPServer::h2int(char c) -{ - if (c >= '0' && c <= '9') - { +unsigned char bell::HTTPServer::h2int(char c) { + if (c >= '0' && c <= '9') { return ((unsigned char)c - '0'); } - if (c >= 'a' && c <= 'f') - { + if (c >= 'a' && c <= 'f') { return ((unsigned char)c - 'a' + 10); } - if (c >= 'A' && c <= 'F') - { + if (c >= 'A' && c <= 'F') { return ((unsigned char)c - 'A' + 10); } return (0); } -std::string bell::HTTPServer::urlDecode(std::string str) -{ +std::string bell::HTTPServer::urlDecode(std::string str) { std::string encodedString = ""; char c; char code0; char code1; - for (int i = 0; i < str.length(); i++) - { + for (int i = 0; i < str.length(); i++) { c = str[i]; - if (c == '+') - { + if (c == '+') { encodedString += ' '; - } - else if (c == '%') - { + } else if (c == '%') { i++; code0 = str[i]; i++; code1 = str[i]; c = (h2int(code0) << 4) | h2int(code1); encodedString += c; - } - else - { + } else { encodedString += c; } @@ -54,23 +40,22 @@ std::string bell::HTTPServer::urlDecode(std::string str) return encodedString; } -std::vector bell::HTTPServer::splitUrl(const std::string &url, char delimiter) -{ +std::vector bell::HTTPServer::splitUrl(const std::string &url, + char delimiter) { std::stringstream ssb(url); std::string segment; std::vector seglist; - while (std::getline(ssb, segment, delimiter)) - { + while (std::getline(ssb, segment, delimiter)) { seglist.push_back(segment); } return seglist; } -void bell::HTTPServer::registerHandler(RequestType requestType, const std::string &routeUrl, httpHandler handler) -{ - if (routes.find(routeUrl) == routes.end()) - { +void bell::HTTPServer::registerHandler(RequestType requestType, + const std::string &routeUrl, + httpHandler handler) { + if (routes.find(routeUrl) == routes.end()) { routes.insert({routeUrl, std::vector()}); } this->routes[routeUrl].push_back(HTTPRoute{ @@ -79,9 +64,9 @@ void bell::HTTPServer::registerHandler(RequestType requestType, const std::strin }); } -void bell::HTTPServer::listen() -{ - BELL_LOG(info, "http", "Starting configuration server at port %d", this->serverPort); +void bell::HTTPServer::listen() { + BELL_LOG(info, "http", "Starting configuration server at port %d", + this->serverPort); // setup address struct addrinfo hints, *server; @@ -91,8 +76,8 @@ void bell::HTTPServer::listen() hints.ai_flags = AI_PASSIVE; getaddrinfo(NULL, std::to_string(serverPort).c_str(), &hints, &server); - int sockfd = socket(server->ai_family, - server->ai_socktype, server->ai_protocol); + int sockfd = + socket(server->ai_family, server->ai_socktype, server->ai_protocol); struct sockaddr_in clientname; socklen_t incomingSockSize; int i; @@ -104,12 +89,12 @@ void bell::HTTPServer::listen() FD_ZERO(&activeFdSet); FD_SET(sockfd, &activeFdSet); - for (;;) - { + for (;;) { /* Block until input arrives on one or more active sockets. */ readFdSet = activeFdSet; - if (select(FD_SETSIZE, &readFdSet, NULL, NULL, NULL) < 0) - { + struct timeval tv = {0, 100000}; + + if (select(FD_SETSIZE, &readFdSet, NULL, NULL, &tv) < 0) { BELL_LOG(error, "http", "Error in select"); perror("select"); // exit(EXIT_FAILURE); @@ -117,118 +102,101 @@ void bell::HTTPServer::listen() /* Service all the sockets with input pending. */ for (i = 0; i < FD_SETSIZE; ++i) - if (FD_ISSET(i, &readFdSet)) - { - if (i == sockfd) - { + if (FD_ISSET(i, &readFdSet)) { + if (i == sockfd) { /* Connection request on original socket. */ int newFd; incomingSockSize = sizeof(clientname); - newFd = accept(sockfd, (struct sockaddr *)&clientname, &incomingSockSize); - if (newFd < 0) - { + newFd = accept(sockfd, (struct sockaddr *)&clientname, + &incomingSockSize); + if (newFd < 0) { perror("accept"); exit(EXIT_FAILURE); } FD_SET(newFd, &activeFdSet); - HTTPConnection conn = { - .buffer = std::vector(128), - .httpMethod = ""}; + HTTPConnection conn = {.buffer = std::vector(128), + .httpMethod = ""}; this->connections.insert({newFd, conn}); - } - else - { + } else { /* Data arriving on an already-connected socket. */ readFromClient(i); } } - for (auto it = this->connections.cbegin(); it != this->connections.cend() /* not hoisted */; /* no increment */) - { - if ((*it).second.toBeClosed) - { + for (auto it = this->connections.cbegin(); + it != this->connections.cend() /* not hoisted */; + /* no increment */) { + if ((*it).second.toBeClosed) { close((*it).first); FD_CLR((*it).first, &activeFdSet); - this->connections.erase(it++); // or "it = m.erase(it)" since C++11 - } - else - { + this->connections.erase( + it++); // or "it = m.erase(it)" since C++11 + } else { ++it; } } } } -void bell::HTTPServer::readFromClient(int clientFd) -{ +void bell::HTTPServer::readFromClient(int clientFd) { HTTPConnection &conn = this->connections[clientFd]; int nbytes = recv(clientFd, &conn.buffer[0], conn.buffer.size(), 0); - if (nbytes < 0) - { + if (nbytes < 0) { BELL_LOG(error, "http", "Error reading from client"); perror("recv"); this->closeConnection(clientFd); - } - else if (nbytes == 0) - { + } else if (nbytes == 0) { this->closeConnection(clientFd); - } - else - { - conn.currentLine += std::string(conn.buffer.data(), conn.buffer.data() + nbytes); + } else { + conn.currentLine += + std::string(conn.buffer.data(), conn.buffer.data() + nbytes); READBODY: - if (!conn.isReadingBody) - { - while (conn.currentLine.find("\r\n") != std::string::npos) - { - auto line = conn.currentLine.substr(0, conn.currentLine.find("\r\n")); - conn.currentLine = conn.currentLine.substr(conn.currentLine.find("\r\n") + 2, conn.currentLine.size()); - if (line.find("GET ") != std::string::npos || line.find("POST ") != std::string::npos || line.find("OPTIONS ") != std::string::npos) - { + if (!conn.isReadingBody) { + while (conn.currentLine.find("\r\n") != std::string::npos) { + auto line = + conn.currentLine.substr(0, conn.currentLine.find("\r\n")); + conn.currentLine = conn.currentLine.substr( + conn.currentLine.find("\r\n") + 2, conn.currentLine.size()); + if (line.find("GET ") != std::string::npos || + line.find("POST ") != std::string::npos || + line.find("OPTIONS ") != std::string::npos) { conn.httpMethod = line; } - if (line.find("Content-Length: ") != std::string::npos) - { - conn.contentLength = std::stoi(line.substr(16, line.size() - 1)); - BELL_LOG(info, "http", "Content-Length: %d", conn.contentLength); + if (line.find("Content-Length: ") != std::string::npos) { + conn.contentLength = + std::stoi(line.substr(16, line.size() - 1)); + BELL_LOG(info, "http", "Content-Length: %d", + conn.contentLength); } - if (line.size() == 0) - { - if (conn.contentLength != 0) - { + if (line.size() == 0) { + if (conn.contentLength != 0) { conn.isReadingBody = true; goto READBODY; - } - else - { - findAndHandleRoute(conn.httpMethod, conn.currentLine, clientFd); + } else { + findAndHandleRoute(conn.httpMethod, conn.currentLine, + clientFd); } } } - } - else - { - if (conn.currentLine.size() >= conn.contentLength) - { + } else { + if (conn.currentLine.size() >= conn.contentLength) { findAndHandleRoute(conn.httpMethod, conn.currentLine, clientFd); } } } } -void bell::HTTPServer::closeConnection(int connection) -{ +void bell::HTTPServer::closeConnection(int connection) { this->connections[connection].toBeClosed = true; } -void bell::HTTPServer::writeResponseEvents(int connFd) -{ +void bell::HTTPServer::writeResponseEvents(int connFd) { std::lock_guard lock(this->responseMutex); std::stringstream stream; @@ -238,8 +206,10 @@ void bell::HTTPServer::writeResponseEvents(int connFd) stream << "Content-type: text/event-stream\r\n"; stream << "Cache-Control: no-cache\r\n"; stream << "Access-Control-Allow-Origin: *\r\n"; - stream << "Access-Control-Allow-Methods: GET, POST, PATCH, PUT, DELETE, OPTIONS\r\n"; - stream << "Access-Control-Allow-Headers: Origin, Content-Type, X-Auth-Token\r\n"; + stream << "Access-Control-Allow-Methods: GET, POST, PATCH, PUT, DELETE, " + "OPTIONS\r\n"; + stream << "Access-Control-Allow-Headers: Origin, Content-Type, " + "X-Auth-Token\r\n"; stream << "\r\n"; auto responseStr = stream.str(); @@ -248,14 +218,12 @@ void bell::HTTPServer::writeResponseEvents(int connFd) this->connections[connFd].isEventConnection = true; } -void bell::HTTPServer::writeResponse(const HTTPResponse &response) -{ +void bell::HTTPServer::writeResponse(const HTTPResponse &response) { std::lock_guard lock(this->responseMutex); auto fileSize = response.body.size(); - if (response.responseReader != nullptr) - { + if (response.responseReader != nullptr) { fileSize = response.responseReader->getTotalSize(); } @@ -265,20 +233,25 @@ void bell::HTTPServer::writeResponse(const HTTPResponse &response) stream << "Connection: close\r\n"; stream << "Content-type: " << response.contentType << "\r\n"; - if (response.useGzip) - { + if (response.useGzip) { stream << "Content-encoding: gzip" << "\r\n"; } - stream << "Content-length:" << fileSize << "\r\n"; stream << "Access-Control-Allow-Origin: *\r\n"; - stream << "Access-Control-Allow-Methods: GET, POST, PATCH, PUT, DELETE, OPTIONS\r\n"; - stream << "Access-Control-Allow-Headers: Origin, Content-Type, X-Auth-Token\r\n"; + stream << "Access-Control-Allow-Methods: GET, POST, DELETE, OPTIONS\r\n"; + stream << "Access-Control-Expose-Headers: Location\r\n"; + stream << "Access-Control-Allow-Headers: Origin, Content-Type, " + "X-Auth-Token\r\n"; + + // go over every item in request->extraHeaders + for (auto &header : response.extraHeaders) { + stream << header << "\r\n"; + } + stream << "\r\n"; - if (response.body.size() > 0) - { + if (response.body.size() > 0) { stream << response.body; } @@ -286,28 +259,40 @@ void bell::HTTPServer::writeResponse(const HTTPResponse &response) write(response.connectionFd, responseStr.c_str(), responseStr.size()); - if (response.responseReader != nullptr) - { + if (response.responseReader != nullptr) { size_t read; - do - { - read = response.responseReader->read(responseBuffer.data(), responseBuffer.size()); - if (read > 0) - { + do { + read = response.responseReader->read(responseBuffer.data(), + responseBuffer.size()); + if (read > 0) { write(response.connectionFd, responseBuffer.data(), read); } } while (read > 0); } + + BELL_LOG(info, "HTTP", "Closing connection"); this->closeConnection(response.connectionFd); } -void bell::HTTPServer::respond(const HTTPResponse &response) -{ +void bell::HTTPServer::respond(const HTTPResponse &response) { writeResponse(response); } -void bell::HTTPServer::publishEvent(std::string eventName, std::string eventData) -{ +void bell::HTTPServer::redirectTo(const std::string & url, int connectionFd) { + std::lock_guard lock(this->responseMutex); + std::stringstream stream; + stream << "HTTP/1.1 301 Moved Permanently\r\n"; + stream << "Server: EUPHONIUM\r\n"; + stream << "Connection: close\r\n"; + stream << "Location: " << url << "\r\n\r\n"; + auto responseStr = stream.str(); + + write(connectionFd, responseStr.c_str(), responseStr.size()); + this->closeConnection(connectionFd); +} + +void bell::HTTPServer::publishEvent(std::string eventName, + std::string eventData) { std::lock_guard lock(this->responseMutex); BELL_LOG(info, "http", "Publishing event"); @@ -317,27 +302,24 @@ void bell::HTTPServer::publishEvent(std::string eventName, std::string eventData auto responseStr = stream.str(); // Reply to all event-connections - for (auto it = this->connections.cbegin(); it != this->connections.cend(); ++it) - { - if ((*it).second.isEventConnection) - { + for (auto it = this->connections.cbegin(); it != this->connections.cend(); + ++it) { + if ((*it).second.isEventConnection) { write(it->first, responseStr.c_str(), responseStr.size()); } } } -std::map bell::HTTPServer::parseQueryString(const std::string &queryString) -{ +std::map +bell::HTTPServer::parseQueryString(const std::string &queryString) { std::map query; auto prefixedString = "&" + queryString; - while (prefixedString.find("&") != std::string::npos) - { + while (prefixedString.find("&") != std::string::npos) { auto keyStart = prefixedString.find("&"); auto keyEnd = prefixedString.find("="); // Find second occurence of "&" in prefixedString auto valueEnd = prefixedString.find("&", keyStart + 1); - if (valueEnd == std::string::npos) - { + if (valueEnd == std::string::npos) { valueEnd = prefixedString.size(); } @@ -350,13 +332,13 @@ std::map bell::HTTPServer::parseQueryString(const std: return query; } -void bell::HTTPServer::findAndHandleRoute(std::string &url, std::string &body, int connectionFd) -{ +void bell::HTTPServer::findAndHandleRoute(std::string &url, std::string &body, + int connectionFd) { std::map pathParams; std::map queryParams; + BELL_LOG(info, "http", "URL %s", url.c_str()); - if (url.find("OPTIONS /") != std::string::npos) - { + if (url.find("OPTIONS /") != std::string::npos) { std::stringstream stream; stream << "HTTP/1.1 200 OK\r\n"; stream << "Server: EUPHONIUM\r\n"; @@ -364,7 +346,8 @@ void bell::HTTPServer::findAndHandleRoute(std::string &url, std::string &body, i stream << "Connection: close\r\n"; stream << "Access-Control-Allow-Origin: *\r\n"; stream << "Access-Control-Allow-Methods: GET, POST, OPTIONS\r\n"; - stream << "Access-Control-Allow-Headers: Origin, Content-Type, X-Auth-Token\r\n"; + stream << "Access-Control-Allow-Headers: Origin, Content-Type, " + "X-Auth-Token\r\n"; stream << "\r\n"; auto responseStr = stream.str(); @@ -374,36 +357,29 @@ void bell::HTTPServer::findAndHandleRoute(std::string &url, std::string &body, i return; } - if (url.find("GET /events") != std::string::npos) - { + if (url.find("GET /events") != std::string::npos) { // Handle SSE endpoint here writeResponseEvents(connectionFd); return; } - for (const auto &routeSet : this->routes) - { - for (const auto &route : routeSet.second) - { + for (const auto &routeSet : this->routes) { + for (const auto &route : routeSet.second) { std::string path = url; - if (url.find("GET ") != std::string::npos && route.requestType == RequestType::GET) - { + if (url.find("GET ") != std::string::npos && + route.requestType == RequestType::GET) { path = path.substr(4); - } - else if (url.find("POST ") != std::string::npos && route.requestType == RequestType::POST) - { + } else if (url.find("POST ") != std::string::npos && + route.requestType == RequestType::POST) { path = path.substr(5); - } - else - { + } else { continue; } path = path.substr(0, path.find(" ")); - if (path.find("?") != std::string::npos) - { + if (path.find("?") != std::string::npos) { auto urlEncodedSplit = splitUrl(path, '?'); path = urlEncodedSplit[0]; queryParams = this->parseQueryString(urlEncodedSplit[1]); @@ -415,46 +391,36 @@ void bell::HTTPServer::findAndHandleRoute(std::string &url, std::string &body, i pathParams.clear(); - if (routeSplit.size() == urlSplit.size()) - { - for (int x = 0; x < routeSplit.size(); x++) - { - if (routeSplit[x] != urlSplit[x]) - { - if (routeSplit[x][0] == ':') - { - pathParams.insert({routeSplit[x].substr(1), urlSplit[x]}); - } - else - { + if (routeSplit.size() == urlSplit.size()) { + for (int x = 0; x < routeSplit.size(); x++) { + if (routeSplit[x] != urlSplit[x]) { + if (routeSplit[x][0] == ':') { + pathParams.insert( + {routeSplit[x].substr(1), urlSplit[x]}); + } else { matches = false; } } } - } - else - { + } else { matches = false; } - if (routeSplit.back().find("*") != std::string::npos && urlSplit[1] == routeSplit[1]) - { + if (routeSplit.back().find("*") != std::string::npos && + urlSplit[1] == routeSplit[1]) { matches = true; } - if (matches) - { - if (body.find("&") != std::string::npos) - { + if (matches) { + if (body.find("&") != std::string::npos) { queryParams = this->parseQueryString(body); } - HTTPRequest req = { - .urlParams = pathParams, - .queryParams = queryParams, - .body = body, - .handlerId = 0, - .connection = connectionFd}; + HTTPRequest req = {.urlParams = pathParams, + .queryParams = queryParams, + .body = body, + .handlerId = 0, + .connection = connectionFd}; route.handler(req); return; diff --git a/components/spotify/cspot/src/AudioChunkManager.cpp b/components/spotify/cspot/src/AudioChunkManager.cpp index de3d619e..073497b8 100644 --- a/components/spotify/cspot/src/AudioChunkManager.cpp +++ b/components/spotify/cspot/src/AudioChunkManager.cpp @@ -114,6 +114,7 @@ void AudioChunkManager::runTask() { } catch (...) { } } else { +// usleep(100*1000); } } diff --git a/components/spotify/cspot/src/MercuryManager.cpp b/components/spotify/cspot/src/MercuryManager.cpp index 7514d4a0..73a9a560 100644 --- a/components/spotify/cspot/src/MercuryManager.cpp +++ b/components/spotify/cspot/src/MercuryManager.cpp @@ -9,7 +9,7 @@ std::map MercuryTypeMap({ {MercuryType::UNSUB, "UNSUB"}, }); -MercuryManager::MercuryManager(std::unique_ptr session): bell::Task("mercuryManager", 8 * 1024, +1, 1) +MercuryManager::MercuryManager(std::unique_ptr session): bell::Task("mercuryManager", 6 * 1024, +1, 1) { this->timeProvider = std::make_shared(); this->callbacks = std::map(); diff --git a/components/spotify/cspot/src/PlainConnection.cpp b/components/spotify/cspot/src/PlainConnection.cpp index f4c43c1d..6a07cccf 100644 --- a/components/spotify/cspot/src/PlainConnection.cpp +++ b/components/spotify/cspot/src/PlainConnection.cpp @@ -5,7 +5,10 @@ #include #include "Logger.h" -PlainConnection::PlainConnection(){}; +PlainConnection::PlainConnection() +{ + this->apSock = -1; +}; PlainConnection::~PlainConnection() { diff --git a/components/spotify/cspot/src/Player.cpp b/components/spotify/cspot/src/Player.cpp index 6ff3ab06..ebfdc1a2 100644 --- a/components/spotify/cspot/src/Player.cpp +++ b/components/spotify/cspot/src/Player.cpp @@ -83,6 +83,8 @@ void Player::runTask() currentTrack->audioStream->streamFinishedCallback = nullptr; currentTrack->audioStream->audioSink = nullptr; currentTrack->audioStream->pcmCallback = nullptr; + } else { + //usleep(100000); } } }