Skip to content

Commit

Permalink
Independent workers using FreeRTOS tasks
Browse files Browse the repository at this point in the history
  • Loading branch information
Ivan Kostoski committed Oct 10, 2019
1 parent 7cb01b0 commit 5565d57
Show file tree
Hide file tree
Showing 14 changed files with 631 additions and 131 deletions.
98 changes: 69 additions & 29 deletions src/HTTPConnection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,23 +26,28 @@ HTTPConnection::~HTTPConnection() {
}

/**
* Initializes the connection from a server socket.
* Initializes the connection
*/
void HTTPConnection::initialize(int serverSocketID, HTTPHeaders *defaultHeaders) {
_defaultHeaders = defaultHeaders;
_serverSocket = serverSocketID;
}

/**
* Accepts 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) {
int HTTPConnection::initialAccept() {
if (_connectionState == STATE_UNDEFINED) {
_defaultHeaders = defaultHeaders;
_socket = accept(serverSocketID, (struct sockaddr * )&_sockAddr, &_addrLen);
_socket = accept(_serverSocket, (struct sockaddr * )&_sockAddr, &_addrLen);

// Build up SSL Connection context if the socket has been created successfully
if (_socket >= 0) {
HTTPS_LOGI("New connection. Socket FID=%d", _socket);
_connectionState = STATE_INITIAL;
_connectionState = STATE_ACCEPTED;
_httpHeaders = new HTTPHeaders();
refreshTimeout();
return _socket;

}

HTTPS_LOGE("Could not accept() new connection");
Expand All @@ -58,6 +63,23 @@ int HTTPConnection::initialize(int serverSocketID, HTTPHeaders *defaultHeaders)
return -1;
}

int HTTPConnection::fullyAccept() {
if (_connectionState == STATE_UNDEFINED) {
initialAccept();
}
if (_connectionState == STATE_ACCEPTED) {
_connectionState = STATE_INITIAL;
return _socket;
}
return -1;
}

/**
* Get connection socket
*/
int HTTPConnection::getSocket() {
return _socket;
}

/**
* True if the connection is timed out.
Expand All @@ -68,6 +90,17 @@ bool HTTPConnection::isTimeoutExceeded() {
return _lastTransmissionTS + HTTPS_CONNECTION_TIMEOUT < millis();
}

/**
* Return remaining milliseconds until timeout
*
* (Should return 0 or negative value if connection is timed-out or closed)
*/
long int HTTPConnection::remainingMsUntilTimeout() {
if (isClosed()) return -1;
unsigned long remain = _lastTransmissionTS + HTTPS_CONNECTION_TIMEOUT - millis();
return (long int)remain;
}

/**
* Resets the timeout to allow again the full HTTPS_CONNECTION_TIMEOUT milliseconds
*/
Expand All @@ -89,6 +122,14 @@ bool HTTPConnection::isError() {
return (_connectionState == STATE_ERROR);
}

bool HTTPConnection::isIdle() {
if (_connectionState == STATE_INITIAL) {
uint32_t delta = millis() - _lastTransmissionTS;
return (int32_t)delta > HTTPS_CONNECTION_IDLE_TIMEOUT;
}
return false;
}

bool HTTPConnection::isSecure() {
return false;
}
Expand Down Expand Up @@ -129,6 +170,7 @@ void HTTPConnection::closeConnection() {
if (_wsHandler != nullptr) {
HTTPS_LOGD("Free WS Handler");
delete _wsHandler;
_wsHandler = NULL;
}
}

Expand Down Expand Up @@ -258,20 +300,19 @@ size_t HTTPConnection::readBytesToBuffer(byte* buffer, size_t length) {
return recv(_socket, buffer, length, MSG_WAITALL | MSG_DONTWAIT);
}

void HTTPConnection::serverError() {
void HTTPConnection::raiseError(uint16_t code, std::string reason) {
_connectionState = STATE_ERROR;

char staticResponse[] = "HTTP/1.1 500 Internal Server Error\r\nServer: esp32https\r\nConnection:close\r\nContent-Type: text/html\r\nContent-Length:34\r\n\r\n<h1>500 Internal Server Error</h1>";
writeBuffer((byte*)staticResponse, strlen(staticResponse));
closeConnection();
}


void HTTPConnection::clientError() {
_connectionState = STATE_ERROR;

char staticResponse[] = "HTTP/1.1 400 Bad Request\r\nServer: esp32https\r\nConnection:close\r\nContent-Type: text/html\r\nContent-Length:26\r\n\r\n<h1>400 Bad Request</h1>";
writeBuffer((byte*)staticResponse, strlen(staticResponse));
std::string sCode = intToString(code);

char headers[] = "\r\nConnection: close\r\nContent-Type: text/plain;charset=utf8\r\n\r\n";
writeBuffer((byte*)"HTTP/1.1 ", 9);
writeBuffer((byte*)sCode.c_str(), sCode.length());
writeBuffer((byte*)" ", 1);
writeBuffer((byte*)(reason.c_str()), reason.length());
writeBuffer((byte*)headers, strlen(headers));
writeBuffer((byte*)sCode.c_str(), sCode.length());
writeBuffer((byte*)" ", 1);
writeBuffer((byte*)(reason.c_str()), reason.length());
closeConnection();
}

Expand All @@ -289,7 +330,7 @@ void HTTPConnection::readLine(int lengthLimit) {
} else {
// Line has not been terminated by \r\n
HTTPS_LOGW("Line without \\r\\n (got only \\r). FID=%d", _socket);
clientError();
raiseError(400, "Bad Request");
return;
}
}
Expand All @@ -301,7 +342,7 @@ void HTTPConnection::readLine(int lengthLimit) {
// Check that the max request string size is not exceeded
if (_parserLine.text.length() > lengthLimit) {
HTTPS_LOGW("Header length exceeded. FID=%d", _socket);
serverError();
raiseError(431, "Request Header Fields Too Large");
return;
}
}
Expand All @@ -319,7 +360,7 @@ void HTTPConnection::signalClientClose() {
*/
void HTTPConnection::signalRequestError() {
// TODO: Check that no response has been transmitted yet
serverError();
raiseError(400, "Bad Request");
}

/**
Expand Down Expand Up @@ -365,7 +406,7 @@ bool HTTPConnection::loop() {
size_t spaceAfterMethodIdx = _parserLine.text.find(' ');
if (spaceAfterMethodIdx == std::string::npos) {
HTTPS_LOGW("Missing space after method");
clientError();
raiseError(400, "Bad Request");
break;
}
_httpMethod = _parserLine.text.substr(0, spaceAfterMethodIdx);
Expand All @@ -374,14 +415,14 @@ bool HTTPConnection::loop() {
size_t spaceAfterResourceIdx = _parserLine.text.find(' ', spaceAfterMethodIdx + 1);
if (spaceAfterResourceIdx == std::string::npos) {
HTTPS_LOGW("Missing space after resource");
clientError();
raiseError(400, "Bad Request");
break;
}
_httpResource = _parserLine.text.substr(spaceAfterMethodIdx + 1, spaceAfterResourceIdx - _httpMethod.length() - 1);

_parserLine.parsingFinished = false;
_parserLine.text = "";
HTTPS_LOGI("Request: %s %s (FID=%d)", _httpMethod.c_str(), _httpResource.c_str(), _socket);
HTTPS_LOGI("Request: %s %s (FID=%d, T=%p)", _httpMethod.c_str(), _httpResource.c_str(), _socket, xTaskGetCurrentTaskHandle());
_connectionState = STATE_REQUEST_FINISHED;
}

Expand Down Expand Up @@ -411,7 +452,7 @@ bool HTTPConnection::loop() {
HTTPS_LOGD("Header: %s = %s (FID=%d)", _parserLine.text.substr(0, idxColon).c_str(), _parserLine.text.substr(idxColon+2).c_str(), _socket);
} else {
HTTPS_LOGW("Malformed request header: %s", _parserLine.text.c_str());
clientError();
raiseError(400, "Bad Request");
break;
}
}
Expand Down Expand Up @@ -558,7 +599,7 @@ bool HTTPConnection::loop() {
} else {
// No match (no default route configured, nothing does match)
HTTPS_LOGW("Could not find a matching resource");
serverError();
raiseError(404, "Not Found");
}

}
Expand Down Expand Up @@ -594,7 +635,6 @@ bool HTTPConnection::loop() {
return (!isClosed() && ((_bufferProcessed < _bufferUnusedIdx) || canReadData()));
}


bool HTTPConnection::checkWebsocket() {
if(_httpMethod == "GET" &&
!_httpHeaders->getValue("Host").empty() &&
Expand Down
20 changes: 14 additions & 6 deletions src/HTTPConnection.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,13 +39,18 @@ class HTTPConnection : private ConnectionContext {
HTTPConnection(ResourceResolver * resResolver);
virtual ~HTTPConnection();

virtual int initialize(int serverSocketID, HTTPHeaders *defaultHeaders);
virtual void initialize(int serverSocketID, HTTPHeaders *defaultHeaders);
virtual int initialAccept();
virtual int fullyAccept();
virtual void closeConnection();
virtual bool isSecure();

bool loop();
bool isClosed();
bool isError();
bool isIdle();
long int remainingMsUntilTimeout();
int getSocket();

protected:
friend class HTTPRequest;
Expand All @@ -57,6 +62,9 @@ class HTTPConnection : private ConnectionContext {
virtual bool canReadData();
virtual size_t pendingByteCount();

// Connection socket (LWIP)
int _socket;

// Timestamp of the last transmission action
unsigned long _lastTransmissionTS;

Expand All @@ -82,6 +90,8 @@ class HTTPConnection : private ConnectionContext {

// The connection has not been established yet
STATE_UNDEFINED,
// The connection fully established (i.e. TLS)
STATE_ACCEPTED,
// The connection has just been created
STATE_INITIAL,
// The request line has been parsed
Expand All @@ -107,8 +117,7 @@ class HTTPConnection : private ConnectionContext {
} _clientState;

private:
void serverError();
void clientError();
void raiseError(uint16_t code, std::string reason);
void readLine(int lengthLimit);

bool isTimeoutExceeded();
Expand All @@ -134,8 +143,8 @@ class HTTPConnection : private ConnectionContext {
// Socket address, length etc for the connection
struct sockaddr _sockAddr;
socklen_t _addrLen;
int _socket;

int _serverSocket;
// Resource resolver used to resolve resources
ResourceResolver * _resResolver;

Expand All @@ -158,7 +167,6 @@ class HTTPConnection : private ConnectionContext {

//Websocket connection
WebsocketHandler * _wsHandler;

};

void handleWebsocketHandshake(HTTPRequest * req, HTTPResponse * res);
Expand Down
29 changes: 24 additions & 5 deletions src/HTTPSConnection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ namespace httpsserver {

HTTPSConnection::HTTPSConnection(ResourceResolver * resResolver):
HTTPConnection(resResolver) {
_sslCtx = NULL;
_ssl = NULL;
_TLSTickets = NULL;
}
Expand All @@ -19,19 +20,33 @@ bool HTTPSConnection::isSecure() {
}

/**
* Initializes the connection from a server socket.
* Initializes the connection with SSL context
*/
void HTTPSConnection::initialize(int serverSocketID, HTTPHeaders *defaultHeaders, SSL_CTX * sslCtx, TLSTickets * tickets) {
HTTPConnection::initialize(serverSocketID, defaultHeaders);
_sslCtx = sslCtx;
_TLSTickets = tickets;
}

/**
* Accepts 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) {
int HTTPSConnection::fullyAccept() {

if (_connectionState == STATE_UNDEFINED) {
// Let the base class connect the plain tcp socket
int resSocket = HTTPConnection::initialize(serverSocketID, defaultHeaders);
initialAccept();
}

if (_connectionState == STATE_ACCEPTED) {
int resSocket = _socket;

// Build up SSL Connection context if the socket has been created successfully
if (resSocket >= 0) {
HTTPS_LOGV("Before SSL accept free:%u, lfb:%u\n", heap_caps_get_free_size(MALLOC_CAP_8BIT), heap_caps_get_largest_free_block(MALLOC_CAP_8BIT));
_ssl = SSL_new(_sslCtx);

_ssl = SSL_new(sslCtx);
if (_TLSTickets != NULL) _TLSTickets->enable(_ssl);

if (_ssl) {
Expand All @@ -42,9 +57,13 @@ int HTTPSConnection::initialize(int serverSocketID, SSL_CTX * sslCtx, HTTPHeader
// Perform the handshake
success = SSL_accept(_ssl);
if (success) {
HTTPS_LOGD("SSL accepted (FID=%d)", resSocket);
HTTPS_LOGV("After SSL accept free:%u, lfb:%u", heap_caps_get_free_size(MALLOC_CAP_8BIT), heap_caps_get_largest_free_block(MALLOC_CAP_8BIT));
_connectionState = STATE_INITIAL;
return resSocket;
} else {
HTTPS_LOGE("SSL_accept failed. Aborting handshake. FID=%d", resSocket);
HTTPS_LOGV("After fail free:%u, lfb:%u", heap_caps_get_free_size(MALLOC_CAP_8BIT), heap_caps_get_largest_free_block(MALLOC_CAP_8BIT));
}
} else {
HTTPS_LOGE("SSL_set_fd failed. Aborting handshake. FID=%d", resSocket);
Expand Down
4 changes: 3 additions & 1 deletion src/HTTPSConnection.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ class HTTPSConnection : public HTTPConnection {
HTTPSConnection(ResourceResolver * resResolver);
virtual ~HTTPSConnection();

virtual int initialize(int serverSocketID, SSL_CTX * sslCtx, HTTPHeaders *defaultHeaders);
virtual void initialize(int serverSocketID, HTTPHeaders *defaultHeaders, SSL_CTX * sslCtx, TLSTickets * tickets);
virtual int fullyAccept() override;
virtual void closeConnection();
virtual bool isSecure();

Expand All @@ -50,6 +51,7 @@ class HTTPSConnection : public HTTPConnection {

private:
// SSL context for this connection
SSL_CTX * _sslCtx;
SSL * _ssl;
TLSTickets * _TLSTickets;

Expand Down
6 changes: 3 additions & 3 deletions src/HTTPSServer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,10 +59,10 @@ void HTTPSServer::teardownSocket() {
_sslctx = NULL;
}

int HTTPSServer::createConnection(int idx) {
HTTPSConnection * HTTPSServer::createConnection() {
HTTPSConnection * newConnection = new HTTPSConnection(this);
_connections[idx] = newConnection;
return newConnection->initialize(_socket, _sslctx, &_defaultHeaders);
newConnection->initialize(_socket, &_defaultHeaders, _sslctx, _TLSTickets);
return newConnection;
}

/**
Expand Down
5 changes: 2 additions & 3 deletions src/HTTPSServer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ class HTTPSServer : public HTTPServer {
// RFC 5077 TLS session tickets
void enableTLSTickets(uint32_t liftimeSeconds = 86400, bool useHardwareRNG = false);

virtual HTTPSConnection * createConnection() override;

private:
// Static configuration. Port, keys, etc. ====================
// Certificate that should be used (includes private key)
Expand All @@ -51,9 +53,6 @@ class HTTPSServer : public HTTPServer {
virtual void teardownSocket();
uint8_t setupSSLCTX();
uint8_t setupCert();

// Helper functions
virtual int createConnection(int idx);
};

} /* namespace httpsserver */
Expand Down
Loading

0 comments on commit 5565d57

Please sign in to comment.