Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Handling of errors - like unstable network - coming via SSL #89

Open
wants to merge 17 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 5 additions & 10 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,18 +1,13 @@
# Changelog

## [pending (master)](https://github.com/fhessel/esp32_https_server/tree/master)
## [v1.1.1](https://github.com/jackjansen/esp32_idf5_https_server/tree/master)

New functionality:


Bug fixes:

- Addressed issues with esp32-arduino-core v3 and esp-idf 5.0

Breaking changes:
## [v1.1.0](https://github.com/jackjansen/esp32_idf5_https_server/tree/master)

- Compatible with ESP-IDF 5.0
- Use esp-tls in stead of openssl

## [v1.0.0](https://github.com/fhessel/esp32_https_server/releases/tag/v1.0.0)

Expand Down
9 changes: 9 additions & 0 deletions example.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[ req ]
distinguished_name = req_distinguished_name
prompt = no
[ req_distinguished_name ]
C = DE
ST = BE
L = Berlin
O = MyCompany
CN = esp32.local
15 changes: 15 additions & 0 deletions example.crt
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
-----BEGIN CERTIFICATE-----
MIICYTCCAcqgAwIBAgIBAjANBgkqhkiG9w0BAQsFADBUMQswCQYDVQQGEwJERTEL
MAkGA1UECAwCQkUxDzANBgNVBAcMBkJlcmxpbjESMBAGA1UECgwJTXlDb21wYW55
MRMwEQYDVQQDDApteWNhLmxvY2FsMB4XDTI0MDcwOTEwMTEwN1oXDTM0MDcwNzEw
MTEwN1owVTELMAkGA1UEBhMCREUxCzAJBgNVBAgMAkJFMQ8wDQYDVQQHDAZCZXJs
aW4xEjAQBgNVBAoMCU15Q29tcGFueTEUMBIGA1UEAwwLZXNwMzIubG9jYWwwgZ8w
DQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAK/aY9jhRfEAgfu3KeTNQ1T2CshDP12e
benmLPnnVG830HQhMLIe1itN/f0tPTguUYtOBgUS9w2zeZEdm6aqeskkkV55Wnmz
IhGNg/cuwl/zp98NbOsZ4Rwcjp5dtL5w8UnlIsK8PsFntRWHQOthgaKH3YzyGg65
ZtPKD13/ZGXbAgMBAAGjQjBAMB0GA1UdDgQWBBTBnWcv/0066SQSwr7nvZc0bg8B
fzAfBgNVHSMEGDAWgBTCqpgyA21IXR4Maq4makkkGgy66TANBgkqhkiG9w0BAQsF
AAOBgQAV0W4470+7/P5bRd94GYyF16/DR1W/H4NITptAJsMGjdyOaRVQnebCYP73
idqMu9LUk0XGqG7StMys5n0VatMjhqOKrZF67wYEgGlaPYTRfpHEDMCPDLB/myYC
OpkxBG45CHzNz2tABRxsfPXN4+8bZpz0y6LUoeQD/ZMcMmZ2sA==
-----END CERTIFICATE-----
4 changes: 2 additions & 2 deletions library.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@
"repository":
{
"type": "git",
"url": "https://github.com/fhessel/esp32_https_server.git"
"url": "https://github.com/bligeti/esp32_https_server.git"
},
"license": "MIT",
"version": "1.0.0",
"version": "1.1.0",
"frameworks": "arduino",
"platforms": ["espressif32"]
}
8 changes: 4 additions & 4 deletions library.properties
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
name=ESP32_HTTPS_Server
version=1.0.0
author=Frank Hessel <frank@fhessel.de>
maintainer=Frank Hessel <frank@fhessel.de>
version=1.1.0
author=bligeti
maintainer=bligeti
sentence=Alternative ESP32 Webserver implementation for the ESP32, supporting HTTPS and HTTP.
paragraph=The library provides TLS support and simultaneous connections. It can be used to run an HTTP or HTTPS server, or both in parallel. The server's resources are defined through handler and middleware functions, giving an easy start to everyone who has worked with frameworks like Express or Servlets before.
category=Communication
url=https://github.com/fhessel/esp32_https_server
url=https://github.com/blgeti/esp32_https_server
architectures=esp32
includes=HTTPSServer.hpp,HTTPRequest.hpp,HTTPResponse.hpp
4 changes: 1 addition & 3 deletions src/ConnectionContext.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,7 @@
#include <IPAddress.h>

// Required for SSL
#include "openssl/ssl.h"
#undef read

#include <esp_tls.h>
namespace httpsserver {

class WebsocketHandler;
Expand Down
49 changes: 33 additions & 16 deletions src/HTTPConnection.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include "HTTPConnection.hpp"

#include "mbedtls/base64.h"
#include "mbedtls/sha1.h"
namespace httpsserver {

HTTPConnection::HTTPConnection(ResourceResolver * resResolver):
Expand Down Expand Up @@ -138,7 +139,8 @@ void HTTPConnection::closeConnection() {
}

if (_wsHandler != nullptr) {
HTTPS_LOGD("Free WS Handler");
HTTPS_LOGD("Free WS Handler in closeConnection()");
_wsHandler->onClose();
delete _wsHandler;
_wsHandler = NULL;
}
Expand Down Expand Up @@ -206,7 +208,7 @@ int HTTPConnection::updateBuffer() {
} else {
// An error occured
_connectionState = STATE_ERROR;
HTTPS_LOGE("An receive error occured, FID=%d", _socket);
HTTPS_LOGE("An receive error occured, FID=%d, SSL_error=%d", _socket, readReturnCode);
closeConnection();
return -1;
}
Expand Down Expand Up @@ -238,6 +240,7 @@ bool HTTPConnection::canReadData() {

size_t HTTPConnection::readBuffer(byte* buffer, size_t length) {
updateBuffer();
if (isClosed()) return 0;
size_t bufferSize = _bufferUnusedIdx - _bufferProcessed;

if (length > bufferSize) {
Expand All @@ -254,7 +257,7 @@ size_t HTTPConnection::readBuffer(byte* buffer, size_t length) {

size_t HTTPConnection::pendingBufferSize() {
updateBuffer();

if (isClosed()) return 0;
return _bufferUnusedIdx - _bufferProcessed + pendingByteCount();
}

Expand Down Expand Up @@ -303,6 +306,10 @@ void HTTPConnection::readLine(int lengthLimit) {
raiseError(400, "Bad Request");
return;
}
} else if (_bufferProcessed+1 == _bufferUnusedIdx){
//Look ahead was not possible so let it go for the next round
_parserLine.possiblenewlineoverflow = true;
return;
}
} else {
_parserLine.text += newChar;
Expand Down Expand Up @@ -389,7 +396,7 @@ void HTTPConnection::loop() {
HTTPS_LOGI("Request: %s %s (FID=%d)", _httpMethod.c_str(), _httpResource.c_str(), _socket);
_connectionState = STATE_REQUEST_FINISHED;
}

if (_parserLine.possiblenewlineoverflow) _parserLine.possiblenewlineoverflow = false;
break;
case STATE_REQUEST_FINISHED: // Read headers

Expand Down Expand Up @@ -422,6 +429,10 @@ void HTTPConnection::loop() {

_parserLine.parsingFinished = false;
_parserLine.text = "";
}
if (_parserLine.possiblenewlineoverflow) {
_parserLine.possiblenewlineoverflow = false;
break;
}
}

Expand Down Expand Up @@ -482,7 +493,8 @@ void HTTPConnection::loop() {

// Find the request handler callback
HTTPSCallbackFunction * resourceCallback;
if (websocketRequested) {
if (websocketRequested && resolvedResource.getMatchingNode()->_nodeType == WEBSOCKET) {
//if (websocketRequested) {
// For the websocket, we use the handshake callback defined below
resourceCallback = &handleWebsocketHandshake;
} else {
Expand Down Expand Up @@ -518,7 +530,7 @@ void HTTPConnection::loop() {
}

// Finally, after the handshake is done, we create the WebsocketHandler and change the internal state.
if(websocketRequested) {
if(websocketRequested && res.getStatusCode() == 101 &&resolvedResource.getMatchingNode()->_nodeType == WEBSOCKET) {
_wsHandler = ((WebsocketNode*)resolvedResource.getMatchingNode())->newHandler();
_wsHandler->initialize(this); // make websocket with this connection
_connectionState = STATE_WEBSOCKET;
Expand Down Expand Up @@ -576,23 +588,28 @@ void HTTPConnection::loop() {
break;
case STATE_WEBSOCKET: // Do handling of the websocket
refreshTimeout(); // don't timeout websocket connection
if(pendingBufferSize() > 0) {
//re-checking the _connectionState since it can change at pendingBufferSize()
if(pendingBufferSize() > 0 && !isClosed() ) {
HTTPS_LOGD("Calling WS handler, FID=%d", _socket);
_wsHandler->loop();
}

// If the client closed the connection unexpectedly
if (_clientState == CSTATE_CLOSED) {
if (_clientState == CSTATE_CLOSED && !isClosed()) {
HTTPS_LOGI("WS lost client, calling onClose, FID=%d", _socket);
_wsHandler->onClose();
}

// If the handler has terminated the connection, clean up and close the socket too
if (_wsHandler->closed() || _clientState == CSTATE_CLOSED) {
HTTPS_LOGI("WS closed, freeing Handler, FID=%d", _socket);
delete _wsHandler;
_wsHandler = nullptr;
_connectionState = STATE_CLOSING;
if (!isClosed()){
if (_wsHandler->closed() || _clientState == CSTATE_CLOSED) {
HTTPS_LOGI("WS closed, freeing Handler, FID=%d", _socket);
delete _wsHandler;
_wsHandler = nullptr;
_connectionState = STATE_CLOSING;
}
} else {
HTTPS_LOGI("WS closed due to SSL level issue and cleanded up");
}
break;
default:;
Expand All @@ -605,7 +622,7 @@ void HTTPConnection::loop() {
bool HTTPConnection::checkWebsocket() {
if(_httpMethod == "GET" &&
!_httpHeaders->getValue("Host").empty() &&
_httpHeaders->getValue("Upgrade") == "websocket" &&
(_httpHeaders->getValue("Upgrade") == "websocket" || _httpHeaders->getValue("Upgrade") == "WebSocket") &&
_httpHeaders->getValue("Connection").find("Upgrade") != std::string::npos &&
!_httpHeaders->getValue("Sec-WebSocket-Key").empty() &&
_httpHeaders->getValue("Sec-WebSocket-Version") == "13") {
Expand Down Expand Up @@ -664,7 +681,7 @@ void handleWebsocketHandshake(HTTPRequest * req, HTTPResponse * res) {
std::string websocketKeyResponseHash(std::string const &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);
mbedtls_sha1((uint8_t*)newKey.data(), newKey.length(), shaData);

// Get output size required for base64 representation
size_t b64BufferSize = 0;
Expand Down
9 changes: 6 additions & 3 deletions src/HTTPConnection.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@

#include <string>
#include <mbedtls/base64.h>
#include <hwcrypto/sha.h>
#include <mbedtls/sha1.h>
#include <sha/sha_parallel_engine.h>
#include <functional>

// Required for sockets
Expand Down Expand Up @@ -107,6 +108,9 @@ class HTTPConnection : private ConnectionContext {
CSTATE_ACTIVE,
CSTATE_CLOSED
} _clientState;

//Websocket connection
WebsocketHandler * _wsHandler;

private:
void raiseError(uint16_t code, std::string reason);
Expand Down Expand Up @@ -144,6 +148,7 @@ class HTTPConnection : private ConnectionContext {
struct {
std::string text = "";
bool parsingFinished = false;
bool possiblenewlineoverflow=false;
} _parserLine;

// HTTP properties: Method, Request, Headers
Expand All @@ -157,8 +162,6 @@ class HTTPConnection : private ConnectionContext {
// Should we use keep alive
bool _isKeepAlive;

//Websocket connection
WebsocketHandler * _wsHandler;

};

Expand Down
3 changes: 2 additions & 1 deletion src/HTTPResponse.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
#undef write
#include <vector>

#include <openssl/ssl.h>
//#include <openssl/ssl.h>
#include <esp_tls.h>

#include "util.hpp"

Expand Down
68 changes: 29 additions & 39 deletions src/HTTPSConnection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,42 +22,33 @@ bool HTTPSConnection::isSecure() {
*
* 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::initialize(int serverSocketID, esp_tls_cfg_server_t * cfgSrv, HTTPHeaders *defaultHeaders) {
if (_connectionState == STATE_UNDEFINED) {
// Let the base class connect the plain tcp socket
int resSocket = HTTPConnection::initialize(serverSocketID, defaultHeaders);

HTTPS_LOGI("Cert len:%d, apn:%s",cfgSrv->servercert_bytes,cfgSrv->alpn_protos[0]);
// Build up SSL Connection context if the socket has been created successfully
if (resSocket >= 0) {

_ssl = esp_tls_init();

_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);
}
if (!_ssl) {
HTTPS_LOGE("esp_tls_init failed. FID=%d", resSocket);
return -1;
}

int res=esp_tls_server_session_create(cfgSrv,resSocket,_ssl);
if (res != 0) {
HTTPS_LOGE("esp_tls_server_session_create failed. FID=%d, Error=%d", resSocket, res);
} else {
HTTPS_LOGE("SSL_new failed. Aborting handshake. FID=%d", resSocket);
return 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();
Expand All @@ -81,21 +72,15 @@ void HTTPSConnection::closeConnection() {
// correctly
_connectionState = STATE_CLOSING;
}

if (this->_wsHandler != nullptr) {
this->_wsHandler->onClose();
}
// 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;
}

esp_tls_server_session_delete(_ssl);
_ssl = NULL;
_connectionState = STATE_ERROR;
}

// If SSL has been brought down, close the socket
Expand All @@ -105,19 +90,24 @@ void HTTPSConnection::closeConnection() {
}

size_t HTTPSConnection::writeBuffer(byte* buffer, size_t length) {
return SSL_write(_ssl, buffer, length);
return esp_tls_conn_write(_ssl,buffer,length);// SSL_write(_ssl, buffer, length);
}

size_t HTTPSConnection::readBytesToBuffer(byte* buffer, size_t length) {
return SSL_read(_ssl, buffer, length);
int ret = esp_tls_conn_read(_ssl, buffer, length);
if (ret < 0) {
//HTTPS_LOGD("SSL_read error: %d", SSL_get_error(_ssl, ret));
HTTPS_LOGD("SSL_read error");
}
return ret;
}

size_t HTTPSConnection::pendingByteCount() {
return SSL_pending(_ssl);
return esp_tls_get_bytes_avail(_ssl);
}

bool HTTPSConnection::canReadData() {
return HTTPConnection::canReadData() || (SSL_pending(_ssl) > 0);
return HTTPConnection::canReadData() || (esp_tls_get_bytes_avail(_ssl) > 0);
}

} /* namespace httpsserver */
Loading