[ Web Proxy ]
URL:
Viewing: https://raw.githubusercontent.com/ZebZeb13/esp32_https_server/master/src/HTTPConnection.cpp [Back]  [Original]

#include "HTTPConnection.hpp"

namespace httpsserver {

HTTPConnection::HTTPConnection(ResourceResolver * resResolver):
	_resResolver(resResolver) {
	_socket = -1;
	_addrLen = 0;

	_bufferProcessed = 0;
	_bufferUnusedIdx = 0;

	_connectionState = STATE_UNDEFINED;
	_clientState = CSTATE_UNDEFINED;
	_httpHeaders = NULL;
	_defaultHeaders = NULL;
	_isKeepAlive = false;
	_lastTransmissionTS = millis();
	_shutdownTS = 0;
	_wsHandler = nullptr;
}

HTTPConnection::~HTTPConnection() {
	// 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 HTTPConnection::initialize(int serverSocketID, 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);
			_connectionState = STATE_INITIAL;
			_httpHeaders = new HTTPHeaders();
			refreshTimeout();
			return _socket;

		}
     
		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  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 HTTPConnection::isTimeoutExceeded() {
	return _lastTransmissionTS + HTTPS_CONNECTION_TIMEOUT < millis();
}

/**
 * Resets the timeout to allow again the full HTTPS_CONNECTION_TIMEOUT milliseconds
 */
void HTTPConnection::refreshTimeout() {
	_lastTransmissionTS = millis();
}

/**
 * Returns true, if the connection has been closed.
 */
bool HTTPConnection::isClosed() {
	return (_connectionState == STATE_ERROR || _connectionState == STATE_CLOSED);
}

/**
 * Returns true, if the connection has been closed due to error
 */
bool HTTPConnection::isError() {
	return (_connectionState == STATE_ERROR);
}

bool HTTPConnection::isSecure() {
	return false;
}

void HTTPConnection::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;
	}

	// Tear down the socket
	if (_socket >= 0) {
		HTTPS_DLOGHEX("[] Websocket detected");
	    return true;
	} else
	   	return false;
}

/**
 * Middleware function that handles the validation of parameters
 */
void validationMiddleware(HTTPRequest * req, HTTPResponse * res, std::function next) {
	bool valid = true;
	// Get the matched node
	HTTPNode * node = req->getResolvedNode();
	// Get the parameters
	ResourceParameters * params = req->getParams();

	// Iterate over the validators and run them
	std::vector * validators = node->getValidators();
	for(std::vector::iterator validator = validators->begin(); valid && validator != validators->end(); ++validator) {
		std::string param = params->getUrlParameter((*validator)->_idx);
    valid = ((*validator)->_validatorFunction)(param);
	}

	if (valid) {
		next();
	} else {
		res->setStatusCode(400);
		res->setStatusText("Bad Request");
		res->print("400 Bad Request");
	}
}

/**
 * Handler function for the websocket handshake. Will be used by HTTPConnection if a websocket is detected
 */
void handleWebsocketHandshake(HTTPRequest * req, HTTPResponse * res) {
	res->setStatusCode(101);
	res->setStatusText("Switching Protocols");
	res->setHeader("Upgrade", "websocket");
	res->setHeader("Connection", "Upgrade");
	res->setHeader("Sec-WebSocket-Accept", websocketKeyResponseHash(req->getHeader("Sec-WebSocket-Key")));
	res->print("");
}

/**
 * Function used to compute the value of the Sec-WebSocket-Accept during Websocket handshake
 */
std::string websocketKeyResponseHash(std::string key) {
	std::string newKey = key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
	uint8_t shaData[HTTPS_SHA1_LENGTH];
	esp_sha(SHA1, (uint8_t*)newKey.data(), newKey.length(), shaData);

	// Get output size required for base64 representation
	size_t b64BufferSize = 0;
	mbedtls_base64_encode(nullptr, 0, &b64BufferSize, (const unsigned char*)shaData, HTTPS_SHA1_LENGTH);

	// Do the real encoding
	unsigned char bufferOut[b64BufferSize];
	size_t bytesEncoded = 0;
	int res = mbedtls_base64_encode(
		bufferOut,
		b64BufferSize,
		&bytesEncoded,
		(const unsigned char*)shaData,
		HTTPS_SHA1_LENGTH
	);
	
	// Check result and return the encoded string
	if (res != 0) {
		return std::string();
	}
	return std::string((char*)bufferOut, bytesEncoded);
} // WebsocketKeyResponseHash

} /* namespace httpsserver */

Web Proxy Viewer  |  New URL  |  Original Page