212 lines
5.2 KiB
C++
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 */
|