#include "HTTPServer.hpp"
namespace httpsserver {
#ifndef HTTPS_DISABLE_IPV4
HTTPServer::HTTPServer(const IPAddress bindAddress, const uint16_t port, const uint8_t maxConnections):
_port(port),
_maxConnections(maxConnections) {
// in_addr_t is an uint32, so no copying here
_bindAddressesV4.push_back(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;
}
#endif
#ifndef HTTPS_DISABLE_IPV6
HTTPServer::HTTPServer(const IPv6Address bindAddress, const uint16_t port, const uint8_t maxConnections):
_port(port),
_maxConnections(maxConnections) {
// IPv6 is a buffer and we don't control bindAddress, so copy it.
_bindAddressesV6.push_back(in6_addr());
memcpy(_bindAddressesV6[0].un.u8_addr, (const uint8_t*)bindAddress, sizeof(in6_addr));
// 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;
}
#endif
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() {
if (_bindAddressesV4.size()>0) {
sockaddr_in *v4addr = (sockaddr_in*)(&_sock_addr);
v4addr->sin_family = AF_INET;
// Listen on all interfaces
v4addr->sin_addr.s_addr = _bindAddressesV4[0];
// Set the server port
v4addr->sin_port = htons(_port);;
} else {
sockaddr_in6 *v6addr = (sockaddr_in6*)(&_sock_addr);
v6addr->sin6_family = AF_INET6;
// Listen on all interfaces
v6addr->sin6_addr = _bindAddressesV6[0];
// Set the server port
v6addr->sin6_port = htons(_port);
}
_socket = socket(_sock_addr.sa_family, SOCK_STREAM, 0);
if (_socket >= 0) {
// 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 */