Files
esp32-fm-pirate/src/http/HTTPServer.cpp
2023-04-22 21:55:43 +03:00

212 lines
5.2 KiB
C++

#include "HTTPServer.hpp"
namespace httpsserver {
HTTPServer::HTTPServer(const uint16_t port, const uint8_t maxConnections, const in_addr_t bindAddress):
_port(port),
_maxConnections(maxConnections),
_bindAddress(bindAddress) {
// Create space for the connections
_connections = new HTTPConnection*[maxConnections];
for(uint8_t i = 0; i < maxConnections; i++) _connections[i] = NULL;
// Configure runtime data
_socket = -1;
_running = false;
}
HTTPServer::~HTTPServer() {
// Stop the server.
// This will also remove all existing connections
if(_running) {
stop();
}
// Delete connection pointers
delete[] _connections;
}
/**
* This method starts the server and begins to listen on the port
*/
uint8_t HTTPServer::start() {
if (!_running) {
if (setupSocket()) {
_running = true;
return 1;
}
return 0;
} else {
return 1;
}
}
bool HTTPServer::isRunning() {
return _running;
}
/**
* This method stops the server
*/
void HTTPServer::stop() {
if (_running) {
// Set the flag that the server is stopped
_running = false;
// Clean up the connections
bool hasOpenConnections = true;
while(hasOpenConnections) {
hasOpenConnections = false;
for(int i = 0; i < _maxConnections; i++) {
if (_connections[i] != NULL) {
_connections[i]->closeConnection();
// Check if closing succeeded. If not, we need to call the close function multiple times
// and wait for the client
if (_connections[i]->isClosed()) {
delete _connections[i];
_connections[i] = NULL;
} else {
hasOpenConnections = true;
}
}
}
delay(1);
}
teardownSocket();
}
}
/**
* Adds a default header that is included in every response.
*
* This could be used for example to add a Server: header or for CORS options
*/
void HTTPServer::setDefaultHeader(std::string name, std::string value) {
_defaultHeaders.set(new HTTPHeader(name, value));
}
/**
* The loop method can either be called by periodical interrupt or in the main loop and handles processing
* of data
*/
void HTTPServer::loop() {
// Only handle requests if the server is still running
if(!_running) return;
// Step 1: Process existing connections
// Process open connections and store the index of a free connection
// (we might use that later on)
int freeConnectionIdx = -1;
for (int i = 0; i < _maxConnections; i++) {
// Fetch a free index in the pointer array
if (_connections[i] == NULL) {
freeConnectionIdx = i;
} else {
// if there is a connection (_connections[i]!=NULL), check if its open or closed:
if (_connections[i]->isClosed()) {
// if it's closed, clean up:
delete _connections[i];
_connections[i] = NULL;
freeConnectionIdx = i;
} else {
// if not, process it:
_connections[i]->loop();
}
}
}
// Step 2: Check for new connections
// This makes only sense if there is space to store the connection
if (freeConnectionIdx > -1) {
// We create a file descriptor set to be able to use the select function
fd_set sockfds;
// Out socket is the only socket in this set
FD_ZERO(&sockfds);
FD_SET(_socket, &sockfds);
// We define a "immediate" timeout
timeval timeout;
timeout.tv_sec = 0;
timeout.tv_usec = 0; // Return immediately, if possible
// Wait for input
// As by 2017-12-14, it seems that FD_SETSIZE is defined as 0x40, but socket IDs now
// start at 0x1000, so we need to use _socket+1 here
select(_socket + 1, &sockfds, NULL, NULL, &timeout);
// There is input
if (FD_ISSET(_socket, &sockfds)) {
int socketIdentifier = createConnection(freeConnectionIdx);
// If initializing did not work, discard the new socket immediately
if (socketIdentifier < 0) {
delete _connections[freeConnectionIdx];
_connections[freeConnectionIdx] = NULL;
}
}
}
}
int HTTPServer::createConnection(int idx) {
HTTPConnection * newConnection = new HTTPConnection(this);
_connections[idx] = newConnection;
return newConnection->initialize(_socket, &_defaultHeaders);
}
/**
* This method prepares the tcp server socket
*/
uint8_t HTTPServer::setupSocket() {
// (AF_INET = IPv4, SOCK_STREAM = TCP)
_socket = socket(AF_INET, SOCK_STREAM, 0);
if (_socket>=0) {
_sock_addr.sin_family = AF_INET;
// Listen on all interfaces
_sock_addr.sin_addr.s_addr = _bindAddress;
// Set the server port
_sock_addr.sin_port = htons(_port);
// Now bind the TCP socket we did create above to the socket address we specified
// (The TCP-socket now listens on 0.0.0.0:port)
int err = bind(_socket, (struct sockaddr* )&_sock_addr, sizeof(_sock_addr));
if(!err) {
err = listen(_socket, _maxConnections);
if (!err) {
return 1;
} else {
close(_socket);
_socket = -1;
return 0;
}
} else {
close(_socket);
_socket = -1;
return 0;
}
} else {
_socket = -1;
return 0;
}
}
void HTTPServer::teardownSocket() {
// Close the actual server sockets
close(_socket);
_socket = -1;
}
} /* namespace httpsserver */