124 lines
3.6 KiB
C++
124 lines
3.6 KiB
C++
#include "HTTPSConnection.hpp"
|
|
|
|
namespace httpsserver {
|
|
|
|
|
|
HTTPSConnection::HTTPSConnection(ResourceResolver * resResolver):
|
|
HTTPConnection(resResolver) {
|
|
_ssl = NULL;
|
|
}
|
|
|
|
HTTPSConnection::~HTTPSConnection() {
|
|
// Close the socket
|
|
closeConnection();
|
|
}
|
|
|
|
bool HTTPSConnection::isSecure() {
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* 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) {
|
|
// Let the base class connect the plain tcp socket
|
|
int resSocket = HTTPConnection::initialize(serverSocketID, defaultHeaders);
|
|
|
|
// Build up SSL Connection context if the socket has been created successfully
|
|
if (resSocket >= 0) {
|
|
|
|
_ssl = SSL_new(sslCtx);
|
|
|
|
if (_ssl) {
|
|
// Bind SSL to the socket
|
|
int success = SSL_set_fd(_ssl, resSocket);
|
|
if (success) {
|
|
|
|
// Perform the handshake
|
|
success = SSL_accept(_ssl);
|
|
if (success) {
|
|
return resSocket;
|
|
} else {
|
|
HTTPS_LOGE("SSL_accept failed. Aborting handshake. FID=%d", resSocket);
|
|
}
|
|
} else {
|
|
HTTPS_LOGE("SSL_set_fd failed. Aborting handshake. FID=%d", resSocket);
|
|
}
|
|
} else {
|
|
HTTPS_LOGE("SSL_new failed. Aborting handshake. FID=%d", resSocket);
|
|
}
|
|
|
|
} else {
|
|
HTTPS_LOGE("Could not accept() new connection. FID=%d", resSocket);
|
|
}
|
|
|
|
_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;
|
|
}
|
|
|
|
|
|
void HTTPSConnection::closeConnection() {
|
|
|
|
// FIXME: Copy from HTTPConnection, could be done better probably
|
|
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_LOGW("SSL_shutdown did not receive close notification from the client");
|
|
_connectionState = STATE_ERROR;
|
|
}
|
|
}
|
|
|
|
// If SSL has been brought down, close the socket
|
|
if (!_ssl) {
|
|
HTTPConnection::closeConnection();
|
|
}
|
|
}
|
|
|
|
size_t HTTPSConnection::writeBuffer(byte* buffer, size_t length) {
|
|
return SSL_write(_ssl, buffer, length);
|
|
}
|
|
|
|
size_t HTTPSConnection::readBytesToBuffer(byte* buffer, size_t length) {
|
|
return SSL_read(_ssl, buffer, length);
|
|
}
|
|
|
|
size_t HTTPSConnection::pendingByteCount() {
|
|
return SSL_pending(_ssl);
|
|
}
|
|
|
|
bool HTTPSConnection::canReadData() {
|
|
return HTTPConnection::canReadData() || (SSL_pending(_ssl) > 0);
|
|
}
|
|
|
|
} /* namespace httpsserver */
|