GitHub Viewer
/*
* HTTPSConnection.cpp
*
* Created on: Dec 6, 2017
* Author: frank
*/
#include "HTTPSConnection.hpp"
namespace httpsserver {
HTTPSConnection::HTTPSConnection(ResourceResolver * resResolver):
_resResolver(resResolver) {
_socket = -1;
_addrLen = 0;
_ssl = NULL;
_bufferProcessed = 0;
_bufferUnusedIdx = 0;
_connectionState = STATE_UNDEFINED;
_clientState = CSTATE_UNDEFINED;
_httpHeaders = NULL;
_defaultHeaders = NULL;
_isKeepAlive = false;
_lastTransmissionTS = millis();
_shutdownTS = 0;
}
HTTPSConnection::~HTTPSConnection() {
// Close the socket
closeConnection();
}
/**
* Initializes the connection from a server socket.
*
* The call WILL BLOCK if accept(serverSocketID) blocks. So use select() to check for that in advance.
*/
int HTTPSConnection::initialize(int serverSocketID, SSL_CTX * sslCtx, HTTPHeaders *defaultHeaders) {
if (_connectionState == STATE_UNDEFINED) {
_defaultHeaders = defaultHeaders;
_socket = accept(serverSocketID, (struct sockaddr * )&_sockAddr, &_addrLen);
// Build up SSL Connection context if the socket has been created successfully
if (_socket >= 0) {
HTTPS_DLOGHEX("[-->] New connection. Socket fid is: ", _socket);
_ssl = SSL_new(sslCtx);
if (_ssl) {
// Bind SSL to the socket
int success = SSL_set_fd(_ssl, _socket);
if (success) {
// Perform the handshake
success = SSL_accept(_ssl);
if (success) {
_connectionState = STATE_INITIAL;
_httpHeaders = new HTTPHeaders();
refreshTimeout();
return _socket;
} else {
HTTPS_DLOG("[ERR] SSL_accept failed. Aborting handshake.");
}
} else {
HTTPS_DLOG("[ERR] SSL_set_fd failed. Aborting handshake.");
}
} else {
HTTPS_DLOG("[ERR] SSL_new failed. Aborting handshake.");
}
} else {
HTTPS_DLOG("[ERR] Could not accept() new connection");
}
_connectionState = STATE_ERROR;
_clientState = CSTATE_ACTIVE;
// This will only be called if the connection could not be established and cleanup
// variables like _ssl etc.
closeConnection();
}
// Error: The connection has already been established or could not be established
return -1;
}
/**
* True if the connection is timed out.
*
* (Should be checkd in the loop and transition should go to CONNECTION_CLOSE if exceeded)
*/
bool HTTPSConnection::isTimeoutExceeded() {
return _lastTransmissionTS + HTTPS_CONNECTION_TIMEOUT < millis();
}
/**
* Resets the timeout to allow again the full HTTPS_CONNECTION_TIMEOUT milliseconds
*/
void HTTPSConnection::refreshTimeout() {
_lastTransmissionTS = millis();
}
/**
* Returns true, if the connection has been closed.
*/
bool HTTPSConnection::isClosed() {
return (_connectionState == STATE_ERROR || _connectionState == STATE_CLOSED);
}
/**
* Returns true, if the connection has been closed due to error
*/
bool HTTPSConnection::isError() {
return (_connectionState == STATE_ERROR);
}
void HTTPSConnection::closeConnection() {
// TODO: Call an event handler here, maybe?
if (_connectionState != STATE_ERROR && _connectionState != STATE_CLOSED) {
// First call to closeConnection - set the timestamp to calculate the timeout later on
if (_connectionState != STATE_CLOSING) {
_shutdownTS = millis();
}
// Set the connection state to closing. We stay in closing as long as SSL has not been shutdown
// correctly
_connectionState = STATE_CLOSING;
}
// Try to tear down SSL while we are in the _shutdownTS timeout period or if an error occurred
if (_ssl) {
if(_connectionState == STATE_ERROR || SSL_shutdown(_ssl) == 0) {
// SSL_shutdown will return 1 as soon as the client answered with close notify
// This means we are safe to close the socket
SSL_free(_ssl);
_ssl = NULL;
} else if (_shutdownTS + HTTPS_SHUTDOWN_TIMEOUT < millis()) {
// The timeout has been hit, we force SSL shutdown now by freeing the context
SSL_free(_ssl);
_ssl = NULL;
HTTPS_DLOG("[ERR] SSL_shutdown did not receive close notification from the client");
_connectionState = STATE_ERROR;
}
}
// If SSL has been brought down, close the socket
if (!_ssl) {
// Tear down the socket
if (_socket >= 0) {
HTTPS_DLOGHEX("[