From ec07ec137927386cd6be72d5ad79729f1b4efd81 Mon Sep 17 00:00:00 2001 From: Tiago Medicci Date: Mon, 5 Feb 2018 16:18:45 -0200 Subject: [PATCH 01/10] Added debug log to verify parsed key-value pairs and fixed context to save parsed key-value pairs from request body --- http_server.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/http_server.c b/http_server.c index 6f2f24c..4393ced 100644 --- a/http_server.c +++ b/http_server.c @@ -388,6 +388,7 @@ const char* http_request_get_arg_value(http_context_t ctx, const char* name) { http_header_t* it; SLIST_FOREACH(it, &ctx->request_args, list_entry) { + ESP_LOGD(TAG, "Key %s: %s", it->name, it->value); if (strcasecmp(name, it->name) == 0) { return it->value; } @@ -415,7 +416,7 @@ static void form_data_handler_cb(http_context_t http_ctx, void* ctx) const char* str; size_t len; http_request_get_data(http_ctx, &str, &len); - parse_urlencoded_args(ctx, str, len); + parse_urlencoded_args(http_ctx, str, len); } } From 7b73643728f8304c09e165fe63db736a0ca6873a Mon Sep 17 00:00:00 2001 From: Tiago Medicci Date: Mon, 12 Feb 2018 19:09:09 -0200 Subject: [PATCH 02/10] GET method example added --- http_server.c | 44 ++++++++++++++++++++++++++++++++++++++++++++ http_server.h | 10 ++++++++++ 2 files changed, 54 insertions(+) diff --git a/http_server.c b/http_server.c index 4393ced..5188f49 100644 --- a/http_server.c +++ b/http_server.c @@ -111,6 +111,22 @@ static esp_err_t add_keyval_pair(http_header_list_t *list, const char* name, con static const char* TAG = "http_server"; +const static char index_html[] = "" + "\n" + "\n" + " \n" + " \n" + "HELLO ESP32\n" + "\n" + "\n" + "

Hello World, from ESP32!

\n" + "\n" + "\n"; + + esp_err_t http_register_handler(http_server_t server, const char* uri_pattern, int method, int events, http_handler_fn_t callback, void* callback_arg) @@ -818,3 +834,31 @@ esp_err_t http_server_stop(http_server_t server) free(server); return ESP_OK; } + +static void cb_GET_method(http_context_t http_ctx, void* ctx) +{ + size_t response_size = strlen(index_html); + http_response_begin(http_ctx, 200, "text/html", response_size); + http_buffer_t http_index_html = { .data = index_html }; + http_response_write(http_ctx, &http_index_html); + http_response_end(http_ctx); +} + +esp_err_t simple_GET_method_example(void) +{ + http_server_t server; + http_server_options_t http_options = HTTP_SERVER_OPTIONS_DEFAULT(); + esp_err_t res; + + res = http_server_start(&http_options, &server); + if (res != ESP_OK) { + return res; + } + + res = http_register_handler(server, "/", HTTP_GET, HTTP_HANDLE_RESPONSE, &cb_GET_method, NULL); + if (res != ESP_OK) { + return res; + } + + return res; +} diff --git a/http_server.h b/http_server.h index 9c109d8..405e3a0 100644 --- a/http_server.h +++ b/http_server.h @@ -285,6 +285,16 @@ esp_err_t http_response_write(http_context_t http_ctx, const http_buffer_t* buff */ esp_err_t http_response_end(http_context_t http_ctx); +/** + * @brief Example of GET method. Responding a simple "Hello World" html. All initializations included. + * @param none + * @return + * - ESP_OK on success + * - other errors in the future? + */ +esp_err_t simple_GET_method_example(void); + + #ifdef __cplusplus } #endif From 58506b60365a34bb0f3376c6924a94a8685efab8 Mon Sep 17 00:00:00 2001 From: Tiago Medicci Date: Tue, 13 Feb 2018 14:30:14 -0200 Subject: [PATCH 03/10] Added example function to documentation --- README.md | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 8fda91e..0b6dce1 100644 --- a/README.md +++ b/README.md @@ -1,38 +1,41 @@ -Simple HTTP server ------------------- +#Simple HTTP server This is a very minimal HTTP server I use in some of the projects. Absolutely not ready for any kind of production use. -How to use ----------- + +#How to use Really, please see above note. This directory is an ESP-IDF component. Clone it (or add it as a submodule) into the component directory of the project. -Documentation -------------- +#Documentation None yet, but I tried to make the comments in the header file helpful. -Example -------- +#Examples + +Examples functions at http server + +## GET Method Example + +`simple_GET_method_example()` function: -None yet. +* Add http_server.c and http_server.h as a component into your project. +* Server initialization added into the example function, simply call it and it should work! +* Receiving a GET request at /, http server response is a "Hello World, from ESP32!" html. -Debugging ---------- +#Debugging Increasing log level to "Verbose" should produce lots of output related to request handling. -License -------- +#License GPL, see [LICENSE](LICENSE) file. Mostly because this is a very early version. Will be relicensed as something more reasonable later. From cf6c707e2e9972e664fed238b9f4a749d2ed5c50 Mon Sep 17 00:00:00 2001 From: Tiago Medicci Date: Tue, 13 Feb 2018 14:35:17 -0200 Subject: [PATCH 04/10] Added spaces between # and title/subtitle at README.md --- README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 0b6dce1..3f0ffe5 100644 --- a/README.md +++ b/README.md @@ -1,23 +1,23 @@ -#Simple HTTP server +# Simple HTTP server This is a very minimal HTTP server I use in some of the projects. Absolutely not ready for any kind of production use. -#How to use +# How to use Really, please see above note. This directory is an ESP-IDF component. Clone it (or add it as a submodule) into the component directory of the project. -#Documentation +# Documentation None yet, but I tried to make the comments in the header file helpful. -#Examples +# Examples Examples functions at http server @@ -30,12 +30,12 @@ Examples functions at http server * Receiving a GET request at /, http server response is a "Hello World, from ESP32!" html. -#Debugging +# Debugging Increasing log level to "Verbose" should produce lots of output related to request handling. -#License +# License GPL, see [LICENSE](LICENSE) file. Mostly because this is a very early version. Will be relicensed as something more reasonable later. From 60cddcc9ca76aba8761d4fa20a04b0bef249298f Mon Sep 17 00:00:00 2001 From: Tiago Medicci Date: Wed, 14 Feb 2018 23:31:27 -0200 Subject: [PATCH 05/10] Added POST example function. --- README.md | 5 +++++ http_server.c | 43 +++++++++++++++++++++++++++++++++++++++++++ http_server.h | 8 ++++++++ 3 files changed, 56 insertions(+) diff --git a/README.md b/README.md index 3f0ffe5..ebc273b 100644 --- a/README.md +++ b/README.md @@ -29,6 +29,11 @@ Examples functions at http server * Server initialization added into the example function, simply call it and it should work! * Receiving a GET request at /, http server response is a "Hello World, from ESP32!" html. +## POST Method Example +`simple_POST_method_example()` function: +* As well as GET example, simply add http_server as a componente into your ESP-IDF project. +* Server initialization added into the POST example function, simply call it and don't worry. +* Post to / a pair of key-value where the key is 'key' and value some value you want to test. The example will show value content. If needed, increade log verbosity at `make menuconfig` to show all parsed key-value pairs. # Debugging diff --git a/http_server.c b/http_server.c index 5188f49..1c788b0 100644 --- a/http_server.c +++ b/http_server.c @@ -126,6 +126,9 @@ const static char index_html[] = "" "\n" "\n"; +const static char response_OK[] = + "OK!\n"; + esp_err_t http_register_handler(http_server_t server, const char* uri_pattern, int method, @@ -862,3 +865,43 @@ esp_err_t simple_GET_method_example(void) return res; } + +static void cb_POST_method(http_context_t http_ctx, void* ctx) +{ + const char* post_data; + + ESP_LOGI(TAG, "Received data from POST method..."); + + /*Receiving key from POST*/ + post_data = http_request_get_arg_value(http_ctx, "key"); + if(post_data!=NULL){ + ESP_LOGI(TAG, "Received %d bytes corresponding to the 'key': %s", strlen(post_data), post_data); + }else{ + ESP_LOGI(TAG, "Received NULL from POST method"); + } + + size_t response_size = strlen(response_OK); + http_response_begin(http_ctx, 201, "text/plain", response_size); + http_buffer_t http_response_OK = { .data = response_OK }; + http_response_write(http_ctx, &http_response_OK); + http_response_end(http_ctx); +} + +esp_err_t simple_POST_method_example(void) +{ + http_server_t server; + http_server_options_t http_options = HTTP_SERVER_OPTIONS_DEFAULT(); + esp_err_t res; + + res = http_server_start(&http_options, &server); + if (res != ESP_OK) { + return res; + } + + res = http_register_form_handler(server, "/", HTTP_POST, HTTP_HANDLE_RESPONSE, &cb_POST_method, NULL); + if (res != ESP_OK) { + return res; + } + + return res; +} diff --git a/http_server.h b/http_server.h index 405e3a0..1ed846b 100644 --- a/http_server.h +++ b/http_server.h @@ -294,6 +294,14 @@ esp_err_t http_response_end(http_context_t http_ctx); */ esp_err_t simple_GET_method_example(void); +/** + * @brief Example of POST method. Send a application/x-www-form-urlencoded pair key-value where the key is 'key' and some value for it. The value is printed and the server responds a 201 code and a OK message. + * @param none + * @return + * - ESP_OK on success + * - other errors in the future? + */ +esp_err_t simple_POST_method_example(void); #ifdef __cplusplus } From 13ead540c81d615d3f2900a4c04201181902705e Mon Sep 17 00:00:00 2001 From: Tiago Medicci Date: Sat, 24 Mar 2018 14:22:06 -0300 Subject: [PATCH 06/10] Functional support for HTTPS server based on mbedTLS server example Functional support for HTTPS server based on mbedTLS server example --- http_server.c => https_server.c | 554 ++++++++++++++++++++++++++++---- http_server.h => https_server.h | 16 + 2 files changed, 512 insertions(+), 58 deletions(-) rename http_server.c => https_server.c (62%) rename http_server.h => https_server.h (96%) diff --git a/http_server.c b/https_server.c similarity index 62% rename from http_server.c rename to https_server.c index 1c788b0..b6ea827 100644 --- a/http_server.c +++ b/https_server.c @@ -15,7 +15,6 @@ * along with this program. If not, see . */ - #include #include #include @@ -32,10 +31,25 @@ #include "lwip/api.h" #include "http_parser.h" -#include "http_server.h" +#include "https_server.h" + +#include "mbedtls/platform.h" +#include "mbedtls/net_sockets.h" +#include "mbedtls/esp_debug.h" +#include "mbedtls/ssl.h" +#include "mbedtls/entropy.h" +#include "mbedtls/ctr_drbg.h" +#include "mbedtls/error.h" +#include "mbedtls/certs.h" + +#if defined(MBEDTLS_SSL_CACHE_C) +#include "mbedtls/ssl_cache.h" +#endif #define HTTP_PARSE_BUF_MAX_LEN 256 +#define MBEDTLS_EXAMPLE_RECV_BUF_LEN 1024 + typedef enum { HTTP_PARSING_URI, //!< HTTP_PARSING_URI HTTP_PARSING_HEADER_NAME, //!< HTTP_PARSING_HEADER_NAME @@ -77,7 +91,6 @@ struct http_context_ { char* uri; char parse_buffer[HTTP_PARSE_BUF_MAX_LEN]; char* request_header_tmp; - struct netconn *conn; http_parser parser; http_header_list_t request_headers; int response_code; @@ -88,18 +101,33 @@ struct http_context_ { const char* data_ptr; size_t data_size; http_header_list_t request_args; +#ifdef HTTPS_SERVER + mbedtls_ssl_context *ssl_conn; + mbedtls_net_context *client_fd; +#else + struct netconn *conn; +#endif }; - struct http_server_context_ { int port; err_t server_task_err; - struct netconn* server_conn; TaskHandle_t task; EventGroupHandle_t start_done; SLIST_HEAD(, http_handler_t) handlers; _lock_t handlers_lock; struct http_context_ connection_context; +#ifdef HTTPS_SERVER + mbedtls_net_context *listen_fd; + mbedtls_entropy_context *entropy; + mbedtls_ctr_drbg_context *ctr_drbg; + mbedtls_ssl_config *conf; + mbedtls_x509_crt *srvcert; + mbedtls_pk_context *pkey; + mbedtls_ssl_cache_context *cache; +#else + struct netconn* server_conn; +#endif }; #define SERVER_STARTED_BIT BIT(0) @@ -196,7 +224,7 @@ static void header_value_done(http_context_t ctx) { const char* value = ctx->parse_buffer; const char* name = ctx->request_header_tmp; - ESP_LOGD(TAG, "Got header: '%s': '%s'", name, value); + ESP_LOGI(TAG, "Got header: '%s': '%s'", name, value); add_keyval_pair(&ctx->request_headers, name, value); free(ctx->request_header_tmp); ctx->request_header_tmp = NULL; @@ -205,6 +233,7 @@ static void header_value_done(http_context_t ctx) static int http_url_cb(http_parser* parser, const char *at, size_t length) { + ESP_LOGD(TAG, "Called %s", __func__); http_context_t ctx = (http_context_t) parser->data; return append_parse_buffer(ctx, at, length); } @@ -223,6 +252,7 @@ static bool invoke_handler(http_context_t ctx, int event) static int http_headers_done_cb(http_parser* parser) { + ESP_LOGD(TAG, "Called %s", __func__); http_context_t ctx = (http_context_t) parser->data; if (ctx->state == HTTP_PARSING_HEADER_VALUE) { header_value_done(ctx); @@ -293,7 +323,7 @@ static void parse_urlencoded_args(http_context_t ctx, const char* str, size_t le value = urldecode(token_start, pos - token_start); state = READING_KEY; token_start = pos + 1; - ESP_LOGD(TAG, "Got request argument, '%s': '%s'", key, value); + ESP_LOGI(TAG, "Got request argument, '%s': '%s'", key, value); add_keyval_pair(&ctx->request_args, key, value); free(key); key = NULL; @@ -303,7 +333,7 @@ static void parse_urlencoded_args(http_context_t ctx, const char* str, size_t le } if (state == READING_VAL) { value = urldecode(token_start, end - token_start); - ESP_LOGD(TAG, "Got request argument, '%s': '%s'", key, value); + ESP_LOGI(TAG, "Got request argument, '%s': '%s'", key, value); add_keyval_pair(&ctx->request_args, key, value); free(key); key = NULL; @@ -321,7 +351,7 @@ static void uri_done(http_context_t ctx) ++query_str; } ctx->uri = strdup(ctx->parse_buffer); - ESP_LOGD(TAG, "Got URI: '%s'", ctx->uri); + ESP_LOGI(TAG, "Got URI: '%s'", ctx->uri); if (query_str) { parse_urlencoded_args(ctx, query_str, strlen(query_str)); } @@ -334,7 +364,7 @@ static void uri_done(http_context_t ctx) static int http_header_name_cb(http_parser* parser, const char *at, size_t length) { - ESP_LOGV(TAG, "%s", __func__); + ESP_LOGD(TAG, "Called %s", __func__); http_context_t ctx = (http_context_t) parser->data; if (ctx->state == HTTP_PARSING_URI) { uri_done(ctx); @@ -348,7 +378,7 @@ static int http_header_name_cb(http_parser* parser, const char *at, size_t lengt static int http_header_value_cb(http_parser* parser, const char *at, size_t length) { - ESP_LOGV(TAG, "%s", __func__); + ESP_LOGD(TAG, "Called %s", __func__); http_context_t ctx = (http_context_t) parser->data; if (ctx->state == HTTP_PARSING_HEADER_NAME) { header_name_done(ctx); @@ -359,7 +389,7 @@ static int http_header_value_cb(http_parser* parser, const char *at, size_t leng static int http_body_cb(http_parser* parser, const char *at, size_t length) { - ESP_LOGV(TAG, "%s", __func__); + ESP_LOGD(TAG, "Called %s", __func__); http_context_t ctx = (http_context_t) parser->data; ctx->data_ptr = at; ctx->data_size = length; @@ -371,7 +401,7 @@ static int http_body_cb(http_parser* parser, const char *at, size_t length) static int http_message_done_cb(http_parser* parser) { - ESP_LOGV(TAG, "%s", __func__); + ESP_LOGD(TAG, "Called %s", __func__); http_context_t ctx = (http_context_t) parser->data; ctx->state = HTTP_REQUEST_DONE; return 0; @@ -407,7 +437,7 @@ const char* http_request_get_arg_value(http_context_t ctx, const char* name) { http_header_t* it; SLIST_FOREACH(it, &ctx->request_args, list_entry) { - ESP_LOGD(TAG, "Key %s: %s", it->name, it->value); + ESP_LOGI(TAG, "Key %s: %s", it->name, it->value); if (strcasecmp(name, it->name) == 0) { return it->value; } @@ -537,12 +567,48 @@ static esp_err_t http_send_response_headers(http_context_t http_ctx) headers_list_clear(&http_ctx->response_headers); +#ifdef HTTPS_SERVER + int ret; + int actual_len; + ESP_LOGI(TAG, "Writing response headers..." ); + + len = strlen(headers_buf); + actual_len = 0; + ret = 0; + do + { + len = len - ret; + ret = mbedtls_ssl_write( http_ctx->ssl_conn, ((const unsigned char *)headers_buf + ret), len); + if( ret == MBEDTLS_ERR_NET_CONN_RESET ) + { + ESP_LOGE(TAG, "ERROR: peer closed the connection\n\n" ); + //FIXME: reset connection + //goto reset; + } + + if( ret < 0 && ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE ) + { + ESP_LOGE(TAG, "ERROR: mbedtls_ssl_write returned %d\n\n", ret ); + //FIXME: close connection + //goto exit; + } + if (ret > 0) + actual_len += ret; + }while( ret < 0 || ret < len ); + + ESP_LOGI(TAG, "%d bytes written:\n%s", actual_len, (char *)headers_buf); + free(headers_buf); + http_ctx->state = HTTP_SENDING_RESPONSE_BODY; + //FIXME: check return code from mbedTLS + return ESP_OK; +#else err_t err = netconn_write(http_ctx->conn, headers_buf, strlen(headers_buf), NETCONN_COPY); free(headers_buf); http_ctx->state = HTTP_SENDING_RESPONSE_BODY; return lwip_err_to_esp_err(err); +#endif } /* Common function called by http_response_begin and http_response_begin_multipart */ @@ -574,21 +640,53 @@ esp_err_t http_response_begin(http_context_t http_ctx, int code, const char* con esp_err_t http_response_write(http_context_t http_ctx, const http_buffer_t* buffer) { + size_t len; + int ret; + esp_err_t err; if (http_ctx->state == HTTP_COLLECTING_RESPONSE_HEADERS) { - esp_err_t err = http_send_response_headers(http_ctx); + err = http_send_response_headers(http_ctx); if (err != ESP_OK) { + ESP_LOGE(TAG, "ERROR: in http_send_response_headers function..."); return err; } } - const int flag = buffer->data_is_persistent ? NETCONN_NOCOPY : NETCONN_COPY; - size_t len = buffer->size ? buffer->size : strlen((const char*) buffer->data); - err_t rc = netconn_write(http_ctx->conn, buffer->data, len, flag); + len = buffer->size ? buffer->size : strlen((const char*) buffer->data); +#ifdef HTTPS_SERVER + ESP_LOGI(TAG, "Writing to client:" ); + ret = 0; + do + { + len = len - ret; + ret = mbedtls_ssl_write( http_ctx->ssl_conn, (buffer->data + ret), len); + if( ret == MBEDTLS_ERR_NET_CONN_RESET ) + { + ESP_LOGE(TAG, "ERROR: peer closed the connection\n\n" ); + //FIXME: reset connection + //goto reset; + } + + if( ret < 0 && ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE ) + { + ESP_LOGE(TAG, "ERROR: mbedtls_ssl_write returned %d\n\n", ret ); + //FIXME: close connection + //goto exit; + } + if (ret > 0) + http_ctx->accumulated_response_size += ret; + }while( ret < 0 || ret < len ); + + ESP_LOGI(TAG, "%d bytes written:%s", http_ctx->accumulated_response_size, (char *)buffer->data); + return ret; +#else + const int flag = buffer->data_is_persistent ? NETCONN_NOCOPY : NETCONN_COPY; + err_t rc = netconn_write(http_ctx->conn, buffer->data, len, flag); if (rc != ESP_OK) { ESP_LOGD(TAG, "netconn_write rc=%d", rc); } else { http_ctx->accumulated_response_size += len; } return lwip_err_to_esp_err(rc); +#endif } @@ -656,12 +754,15 @@ esp_err_t http_response_set_header(http_context_t http_ctx, const char* name, co static void http_send_not_found_response(http_context_t http_ctx) { - http_response_begin(http_ctx, 404, "text/plain", HTTP_RESPONSE_SIZE_UNKNOWN); + ESP_LOGD(TAG, "Called %s", __func__); + http_response_begin(http_ctx, 404, "text/plain", HTTP_RESPONSE_SIZE_UNKNOWN); const http_buffer_t buf = { .data = "Not found", .data_is_persistent = true }; + ESP_LOGD(TAG, "Calling http_response_write function..."); http_response_write(http_ctx, &buf); + ESP_LOGD(TAG, "Calling http_response_end function..."); http_response_end(http_ctx); } @@ -682,19 +783,21 @@ static const char* http_response_code_to_str(int code) } -static void http_handle_connection(http_server_t server, struct netconn *conn) +static void http_handle_connection(http_server_t server, void *arg_conn) { - struct netbuf *inbuf = NULL; - char *buf; - u16_t buflen; - err_t err = ERR_OK; - + unsigned char *buf; /* Single threaded server, one context only */ http_context_t ctx = &server->connection_context; /* Initialize context */ ctx->state = HTTP_PARSING_URI; - ctx->conn = conn; +#ifdef HTTPS_SERVER +#else + struct netbuf *inbuf = NULL; + u16_t buflen; + err_t err = ERR_OK; + ctx->conn = (struct netconn *)arg_conn; +#endif http_parser_init(&ctx->parser, HTTP_REQUEST); ctx->parser.data = ctx; ctx->server = server; @@ -708,50 +811,359 @@ static void http_handle_connection(http_server_t server, struct netconn *conn) .on_message_complete = &http_message_done_cb }; - while (ctx->state != HTTP_REQUEST_DONE) { - err = netconn_recv(conn, &inbuf); - if (err != ERR_OK) { - break; - } - - err = netbuf_data(inbuf, (void**) &buf, &buflen); - if (err != ERR_OK) { - break; - } - - size_t parsed_bytes = http_parser_execute(&ctx->parser, &parser_settings, buf, buflen); - if (parsed_bytes < buflen) { - break; - } - } +#ifdef HTTPS_SERVER + int ret; + size_t parsed_bytes = 0; + /* + * 6. Read the HTTP Request + */ + + ret = 0; + while (ctx->state != HTTP_REQUEST_DONE) { + ESP_LOGV(TAG, "Reading from client..." ); + buf = malloc(sizeof(char)*MBEDTLS_EXAMPLE_RECV_BUF_LEN); + memset( buf, 0, sizeof(char)*MBEDTLS_EXAMPLE_RECV_BUF_LEN); + //FIXME: add support for buffer > MBEDTLS_EXAMPLE_RECV_BUF_LEN + ret = mbedtls_ssl_read( server->connection_context.ssl_conn, buf, MBEDTLS_EXAMPLE_RECV_BUF_LEN); + + if( ret == MBEDTLS_ERR_SSL_WANT_READ || ret == MBEDTLS_ERR_SSL_WANT_WRITE ) + continue; + + if( ret <= 0 ) + { + switch( ret ) + { + case MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY: + ESP_LOGW(TAG, "Error: connection was closed gracefully" ); + break; + + case MBEDTLS_ERR_NET_CONN_RESET: + ESP_LOGW(TAG, "Error: connection was reset by peer" ); + break; + + default: + ESP_LOGW(TAG, "Error: mbedtls_ssl_read returned -0x%x\n", -ret ); + break; + } + + break; + } + + ESP_LOGD(TAG, "%d bytes read: \n%s", ret, (char *) buf ); + + ESP_LOGI(TAG, "Calling http_parser_execute..."); + parsed_bytes = http_parser_execute(&ctx->parser, &parser_settings, (char *)buf, ret); + } + ESP_LOGD(TAG, "Read looping return: %d", parsed_bytes); + +#else //HTPPS SERVER OFF + while (ctx->state != HTTP_REQUEST_DONE) { + err = netconn_recv(ctx->conn, &inbuf); + if (err != ERR_OK) { + break; + } + + err = netbuf_data(inbuf, (void**) &buf, &buflen); + if (err != ERR_OK) { + break; + } + + size_t parsed_bytes = http_parser_execute(&ctx->parser, &parser_settings, (char *)buf, buflen); + if (parsed_bytes < buflen) { + break; + } + } +#endif - if (err == ERR_OK) { +#ifdef HTTPS_SERVER + if (ret > 0) { ctx->state = HTTP_COLLECTING_RESPONSE_HEADERS; if (ctx->handler == NULL) { - http_send_not_found_response(ctx); + ESP_LOGD(TAG, "No registered Handler!") + http_send_not_found_response(ctx); + } else { - invoke_handler(ctx, HTTP_HANDLE_RESPONSE); + ESP_LOGD(TAG, "Registered Handler Found!") + invoke_handler(ctx, HTTP_HANDLE_RESPONSE); } } +#else + if (err == ERR_OK) { + ctx->state = HTTP_COLLECTING_RESPONSE_HEADERS; + if (ctx->handler == NULL) { + ESP_LOGD(TAG, "No registered Handler!") + http_send_not_found_response(ctx); + } else { + ESP_LOGD(TAG, "Registered Handler Found!") + invoke_handler(ctx, HTTP_HANDLE_RESPONSE); + } + } +#endif headers_list_clear(&ctx->request_headers); headers_list_clear(&ctx->request_args); - free(ctx->uri); ctx->uri = NULL; ctx->handler = NULL; + +#ifdef HTTPS_SERVER + ESP_LOGI(TAG, "Closing the connection..." ); + while( ( ret = mbedtls_ssl_close_notify( server->connection_context.ssl_conn) ) < 0 ) + { + if( ret != MBEDTLS_ERR_SSL_WANT_READ && + ret != MBEDTLS_ERR_SSL_WANT_WRITE ) + { + ESP_LOGI(TAG, "ERROR: mbedtls_ssl_close_notify returned %d\n\n", ret ); + break; + //FIXME: Reset connection + //goto reset; + } + } + ESP_LOGI(TAG, "OK"); +#else if (err != ERR_CLSD) { - netconn_close(conn); + netconn_close(ctx->conn); } if (inbuf) { netbuf_delete(inbuf); } +#endif } static void http_server(void *arg) { http_server_t ctx = (http_server_t) arg; +#ifdef HTTPS_SERVER + char *error_buf; + ESP_LOGV(TAG, "Declaring local mbedTLS context on task..."); + int ret; + mbedtls_net_context listen_fd; + mbedtls_entropy_context entropy; + mbedtls_ctr_drbg_context ctr_drbg; + mbedtls_ssl_context ssl_conn; + mbedtls_ssl_config conf; + mbedtls_x509_crt srvcert; + mbedtls_pk_context pkey; +#if defined(MBEDTLS_SSL_CACHE_C) + mbedtls_ssl_cache_context cache; + (ctx->cache) = &cache; +#endif + (ctx->listen_fd) = &listen_fd; + (ctx->entropy) = &entropy; + (ctx->ctr_drbg) = &ctr_drbg; + (ctx->connection_context.ssl_conn) = &ssl_conn; + (ctx->conf) = &conf; + (ctx->srvcert) = &srvcert; + (ctx->pkey) = &pkey; + + ESP_LOGV(TAG, "Reading CA certificate......"); + extern const unsigned char cacert_pem_start[] asm("_binary_cacert_pem_start"); + extern const unsigned char cacert_pem_end[] asm("_binary_cacert_pem_end"); + const unsigned int cacert_pem_bytes = cacert_pem_end - cacert_pem_start; + + ESP_LOGV(TAG, "Reading Private Key......"); + extern const unsigned char prvtkey_pem_start[] asm("_binary_prvtkey_pem_start"); + extern const unsigned char prvtkey_pem_end[] asm("_binary_prvtkey_pem_end"); + const unsigned int prvtkey_pem_bytes = prvtkey_pem_end - prvtkey_pem_start; + + ESP_LOGV(TAG, "Setting mbedTLS context......"); + mbedtls_net_init( ctx->listen_fd ); + ESP_LOGV(TAG, "OK"); + + ESP_LOGV(TAG, "SSL server context create ......"); + mbedtls_ssl_init( ctx->connection_context.ssl_conn ); + ESP_LOGV(TAG, "OK"); + + ESP_LOGV(TAG, "SSL conf context create ......"); + mbedtls_ssl_config_init( ctx->conf ); + ESP_LOGV(TAG, "OK"); + +#if defined(MBEDTLS_SSL_CACHE_C) + mbedtls_ssl_cache_init( &cache ); +#endif + mbedtls_x509_crt_init( ctx->srvcert ); + mbedtls_pk_init( ctx->pkey ); + mbedtls_entropy_init( ctx->entropy ); + mbedtls_ctr_drbg_init( ctx->ctr_drbg ); + + /* + * 1. Load the certificates and private RSA key + */ + ESP_LOGD(TAG, "Loading the server cert. and key..." ); + /* + * This demonstration program uses embedded test certificates. + * Instead, you may want to use mbedtls_x509_crt_parse_file() to read the + * server and CA certificates, as well as mbedtls_pk_parse_keyfile(). + */ + ESP_LOGV(TAG, "SSL server context set own certification......"); + ESP_LOGV(TAG, "Parsing test srv_crt......"); + ret = mbedtls_x509_crt_parse( ctx->srvcert, (const unsigned char *) cacert_pem_start, + cacert_pem_bytes ); + if( ret != ERR_OK ) + { + ESP_LOGE(TAG, "ERROR: mbedtls_x509_crt_parse returned %d", ret ); + goto exit; + } + ESP_LOGV(TAG, "OK"); + + ESP_LOGV(TAG, "SSL server context set private key......"); + ret = mbedtls_pk_parse_key( ctx->pkey, (const unsigned char *) prvtkey_pem_start, + prvtkey_pem_bytes, NULL, 0 ); + if( ret != ERR_OK ) + { + ESP_LOGE(TAG, "ERROR: mbedtls_pk_parse_key returned %d", ret ); + goto exit; + } + ESP_LOGV(TAG, "OK"); + + /* + * 3. Seed the RNG + */ + ESP_LOGV(TAG, "Seeding the random number generator..." ); + if( ( ret = mbedtls_ctr_drbg_seed( ctx->ctr_drbg, mbedtls_entropy_func, ctx->entropy, + (const unsigned char *) TAG, + strlen( TAG ) ) ) != 0 ) + { + ESP_LOGE(TAG, "ERROR: mbedtls_ctr_drbg_seed returned %d", ret ); + goto exit; + } + ESP_LOGV(TAG, "OK"); + + /* + * 2. Setup the listening TCP socket + */ + char *port = malloc(sizeof(char) * 6); + ESP_LOGV(TAG, "SSL server socket bind at localhost: %s ......", itoa(ctx->port, port,10)); + if( ( ret = mbedtls_net_bind( ctx->listen_fd, NULL, itoa(ctx->port, port,10), MBEDTLS_NET_PROTO_TCP ) ) != 0 ) + { + ESP_LOGE(TAG, "ERROR: mbedtls_net_bind returned %d", ret ); + goto exit; + } + free(port); + ESP_LOGV(TAG, "OK"); + + + /* + * 4. Setup stuff + */ + ESP_LOGV(TAG, "Setting up the SSL conf data...." ); +#ifdef CONFIG_MBEDTLS_DEBUG + mbedtls_esp_enable_debug_log(ctx->conf, 4); +#endif + if( ( ret = mbedtls_ssl_config_defaults( ctx->conf, + MBEDTLS_SSL_IS_SERVER, + MBEDTLS_SSL_TRANSPORT_STREAM, + MBEDTLS_SSL_PRESET_DEFAULT ) ) != 0 ) + { + ESP_LOGE(TAG, "ERROR: mbedtls_ssl_config_defaults returned %d", ret ); + goto exit; + } + + mbedtls_ssl_conf_rng( ctx->conf, mbedtls_ctr_drbg_random, ctx->ctr_drbg ); + +#if defined(MBEDTLS_SSL_CACHE_C) + mbedtls_ssl_conf_session_cache( ctx->conf, ctx->cache, + mbedtls_ssl_cache_get, + mbedtls_ssl_cache_set ); +#endif + + mbedtls_ssl_conf_ca_chain( ctx->conf, (*ctx->srvcert).next, NULL ); + if( ( ret = mbedtls_ssl_conf_own_cert( ctx->conf, ctx->srvcert, ctx->pkey ) ) != 0 ) + { + ESP_LOGE(TAG, "ERROR: mbedtls_ssl_conf_own_cert returned %d", ret ); + goto exit; + } + + if( ( ret = mbedtls_ssl_setup( ctx->connection_context.ssl_conn, ctx->conf ) ) != 0 ) + { + ESP_LOGE(TAG, "ERROR: mbedtls_ssl_setup returned %d", ret ); + goto exit; + } + ESP_LOGV(TAG, "OK"); + + xEventGroupSetBits(ctx->start_done, SERVER_STARTED_BIT); + +reset: + ESP_LOGI(TAG, "mbedTLS HTTPS server is running! Waiting for new connection..."); + do { + mbedtls_net_context client_fd; + (ctx->connection_context.client_fd) = &client_fd; + mbedtls_net_init( ctx->connection_context.client_fd ); + +#ifdef MBEDTLS_ERROR_C + if( ret != ERR_OK ) + { + error_buf = malloc(sizeof(char)*ERROR_BUF_LENGTH); + mbedtls_strerror( ctx->server_task_err, error_buf, sizeof(char)*ERROR_BUF_LENGTH ); + ESP_LOGE(TAG, "Error %d: %s", ret, error_buf ); + free(error_buf); + } +#endif + + mbedtls_net_free( ctx->connection_context.client_fd ); + + mbedtls_ssl_session_reset( ctx->connection_context.ssl_conn ); + /* + * 3. Wait until a client connects + */ + ESP_LOGV(TAG, "Wait until a client connects..." ); + if( ( ret = mbedtls_net_accept( ctx->listen_fd, ctx->connection_context.client_fd, + NULL, 0, NULL ) ) != 0 ) + { + ESP_LOGE(TAG, "ERROR: mbedtls_net_accept returned %d", ret ); + goto exit; + } + mbedtls_ssl_set_bio( ctx->connection_context.ssl_conn, ctx->connection_context.client_fd, mbedtls_net_send, mbedtls_net_recv, NULL ); + ESP_LOGV(TAG, "OK"); + + /* + * 5. Handshake + */ + ESP_LOGV(TAG, "Performing the SSL/TLS handshake..." ); + while( ( ret = mbedtls_ssl_handshake( ctx->connection_context.ssl_conn ) ) != ERR_OK ) + { + if( ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE ) + { + ESP_LOGE(TAG, "ERROR: bedtls_ssl_handshake returned %d", ret ); + goto reset; + } + } + ESP_LOGV(TAG, "OK"); + + ESP_LOGV(TAG, "Handling connection..." ); + if (ret == ERR_OK) { + http_handle_connection(ctx, NULL); + } + ESP_LOGV(TAG, "OK"); + } while (ret == ERR_OK); + +exit: +if (ret != ERR_OK) { + error_buf = malloc(sizeof(char)*ERROR_BUF_LENGTH); + mbedtls_strerror( ctx->server_task_err, error_buf, sizeof(char)*ERROR_BUF_LENGTH ); + ESP_LOGE(TAG, "Error %d: %s", ret, error_buf ); + free(error_buf); + ctx->server_task_err = ret; + xEventGroupSetBits(ctx->start_done, SERVER_DONE_BIT); +} + + mbedtls_net_free( ctx->connection_context.client_fd ); + mbedtls_net_free( ctx->listen_fd ); + mbedtls_x509_crt_free( ctx->srvcert ); + mbedtls_pk_free( ctx->pkey ); + mbedtls_ssl_free( ctx->connection_context.ssl_conn ); + mbedtls_ssl_config_free( ctx->conf ); +#if defined(MBEDTLS_SSL_CACHE_C) + mbedtls_ssl_cache_free( ctx->cache ); +#endif + mbedtls_ctr_drbg_free( ctx->ctr_drbg ); + mbedtls_entropy_free( ctx->entropy ); + + ESP_LOGE(TAG, "Closing Task"); + vTaskDelete(NULL); + +#else struct netconn *client_conn; err_t err; ctx->server_conn = netconn_new(NETCONN_TCP); @@ -778,17 +1190,17 @@ static void http_server(void *arg) netconn_delete(client_conn); } } while (err == ERR_OK); - out: if (ctx->server_conn) { netconn_close(ctx->server_conn); netconn_delete(ctx->server_conn); } if (err != ERR_OK) { - ctx->server_task_err = err; - xEventGroupSetBits(ctx->start_done, SERVER_DONE_BIT); - } - vTaskDelete(NULL); + ctx->server_task_err = err; + xEventGroupSetBits(ctx->start_done, SERVER_DONE_BIT); + } + vTaskDelete(NULL); +#endif } esp_err_t http_server_start(const http_server_options_t* options, http_server_t* out_server) @@ -805,6 +1217,7 @@ esp_err_t http_server_start(const http_server_options_t* options, http_server_t* return ESP_ERR_NO_MEM; } + ESP_LOGV(TAG, "Creating http_server task..."); int ret = xTaskCreatePinnedToCore(&http_server, "httpd", options->task_stack_size, ctx, options->task_priority, @@ -815,16 +1228,25 @@ esp_err_t http_server_start(const http_server_options_t* options, http_server_t* free(ctx); return ESP_ERR_NO_MEM; } + ESP_LOGI(TAG, "Task has been created!"); + ESP_LOGV(TAG, "Checking server status..."); int bits = xEventGroupWaitBits(ctx->start_done, SERVER_STARTED_BIT | SERVER_DONE_BIT, 0, 0, portMAX_DELAY); if (bits & SERVER_DONE_BIT) { - /* Error happened, task is deleted */ + ESP_LOGE(TAG, "SERVER_DONE_BIT Error..."); +#ifdef HTTPS_SERVER + char *error_buf = malloc(sizeof(char)*ERROR_BUF_LENGTH); + mbedtls_strerror( ctx->server_task_err, error_buf, 100 ); + ESP_LOGE(TAG, "Error %d: %s", ret, error_buf ); + free(error_buf); +#endif esp_err_t err = lwip_err_to_esp_err(ctx->server_task_err); vEventGroupDelete(ctx->start_done); free(ctx); return err; } + ESP_LOGI(TAG, "Server started!"); *out_server = ctx; return ESP_OK; } @@ -832,7 +1254,11 @@ esp_err_t http_server_start(const http_server_options_t* options, http_server_t* esp_err_t http_server_stop(http_server_t server) { /* FIXME: figure out a thread safe way to do this */ - netconn_close(server->server_conn); +#ifdef HTTPS_SERVER + /* FIXME: Add function to stop HTTPS */ +#else + netconn_close(server->server_conn); +#endif xEventGroupWaitBits(server->start_done, SERVER_DONE_BIT, 0, 0, portMAX_DELAY); free(server); return ESP_OK; @@ -850,18 +1276,26 @@ static void cb_GET_method(http_context_t http_ctx, void* ctx) esp_err_t simple_GET_method_example(void) { http_server_t server; +#ifdef HTTPS_SERVER + http_server_options_t http_options = HTTPS_SERVER_OPTIONS_DEFAULT(); +#else http_server_options_t http_options = HTTP_SERVER_OPTIONS_DEFAULT(); +#endif esp_err_t res; - res = http_server_start(&http_options, &server); + ESP_LOGI(TAG, "Creating Example Server!"); + ESP_ERROR_CHECK( res = http_server_start(&http_options, &server) ); if (res != ESP_OK) { return res; } + ESP_LOGV(TAG, "OK"); - res = http_register_handler(server, "/", HTTP_GET, HTTP_HANDLE_RESPONSE, &cb_GET_method, NULL); + ESP_LOGI(TAG, "Registering Handler!"); + ESP_ERROR_CHECK( res = http_register_handler(server, "/", HTTP_GET, HTTP_HANDLE_RESPONSE, &cb_GET_method, NULL) ); if (res != ESP_OK) { return res; } + ESP_LOGV(TAG, "OK"); return res; } @@ -890,15 +1324,19 @@ static void cb_POST_method(http_context_t http_ctx, void* ctx) esp_err_t simple_POST_method_example(void) { http_server_t server; +#ifdef HTTPS_SERVER + http_server_options_t http_options = HTTPS_SERVER_OPTIONS_DEFAULT(); +#else http_server_options_t http_options = HTTP_SERVER_OPTIONS_DEFAULT(); +#endif esp_err_t res; - res = http_server_start(&http_options, &server); + ESP_ERROR_CHECK( res = http_server_start(&http_options, &server) ); if (res != ESP_OK) { return res; } - res = http_register_form_handler(server, "/", HTTP_POST, HTTP_HANDLE_RESPONSE, &cb_POST_method, NULL); + ESP_ERROR_CHECK( res = http_register_form_handler(server, "/", HTTP_POST, HTTP_HANDLE_RESPONSE, &cb_POST_method, NULL) ); if (res != ESP_OK) { return res; } diff --git a/http_server.h b/https_server.h similarity index 96% rename from http_server.h rename to https_server.h index 1ed846b..f2a2216 100644 --- a/http_server.h +++ b/https_server.h @@ -27,6 +27,8 @@ extern "C" { * @brief Simple HTTP server */ +#include "sdkconfig.h" + /* Pull in the definitions of HTTP methods */ #include "http_parser.h" @@ -38,6 +40,12 @@ extern "C" { #define HTTP_HANDLE_DATA BIT(2) /*!< Called each time a fragment of request body is received */ #define HTTP_HANDLE_RESPONSE BIT(3) /*!< Called at the end of the request to produce the response */ +/** Error buffer length */ +#define ERROR_BUF_LENGTH 100 + +/** Uncomment to enable secure server */ +#define HTTPS_SERVER + /** Opaque type representing single HTTP connection */ typedef struct http_context_* http_context_t; @@ -65,6 +73,14 @@ typedef struct { .task_priority = 1, \ } +/** Default initializer for http_server_options_t */ +#define HTTPS_SERVER_OPTIONS_DEFAULT() {\ + .port = 443, \ + .task_affinity = tskNO_AFFINITY, \ + .task_stack_size = 10240, \ + .task_priority = 8, \ +} + /** * @brief initialize HTTP server, start listening * @param options pointer to http server options, can point to a temporary From 711bf9694fc752f96c9e411907b617f65dc39d71 Mon Sep 17 00:00:00 2001 From: Tiago Medicci Date: Sat, 24 Mar 2018 14:43:45 -0300 Subject: [PATCH 07/10] New documentation at README.md --- README.md | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index ebc273b..4ee6bc3 100644 --- a/README.md +++ b/README.md @@ -1,25 +1,19 @@ -# Simple HTTP server - -This is a very minimal HTTP server I use in some of the projects. - -Absolutely not ready for any kind of production use. +# Simple HTTP/HTTPS server +This is a very minimal HTTP server. Optionally supports HTTPS server based on mbedTLS server example. # How to use -Really, please see above note. - This directory is an ESP-IDF component. Clone it (or add it as a submodule) into the component directory of the project. - +Enable TLS/SSL HTTPS server by uncommenting the `#define HTTPS_SERVER` line at `https_server.h`, otherwise it will implement unsecured http server. Server certificate and private key are loaded at `https_server.c` directly into flash code as in the [`open_ssl_server`](https://github.com/espressif/esp-idf/tree/master/examples/protocols/openssl_server) example from ESP-IDF repository. Don't forget to insert [`cacert.pem`](https://github.com/espressif/esp-idf/blob/master/examples/protocols/openssl_server/main/cacert.pem) and [`prvtkey.pem`](https://github.com/espressif/esp-idf/blob/master/examples/protocols/openssl_server/main/prvtkey.pem) at your main application directory if you want to run GET/POST examples. # Documentation None yet, but I tried to make the comments in the header file helpful. - # Examples -Examples functions at http server +Examples functions at http server: ## GET Method Example @@ -42,5 +36,4 @@ Increasing log level to "Verbose" should produce lots of output related to reque # License -GPL, see [LICENSE](LICENSE) file. Mostly because this is a very early version. Will be relicensed as something more reasonable later. - +GPL, see [LICENSE](LICENSE) file. Mostly because this is a very early version. Will be relicensed as something more reasonable later. mbedTLS are Apache 2.0 licensed. From ebcb48cc1343e9b7bef4552c3dfcd491de66fdd3 Mon Sep 17 00:00:00 2001 From: Tiago Medicci Date: Sun, 27 May 2018 18:59:27 -0300 Subject: [PATCH 08/10] Added GET/POST examples, certificate chain (server/root), SSL Session Tickets and new README. --- https_server.c => http_server.c | 645 +++++++++++++++++--------------- https_server.h => http_server.h | 19 + 2 files changed, 363 insertions(+), 301 deletions(-) rename https_server.c => http_server.c (71%) rename https_server.h => http_server.h (95%) diff --git a/https_server.c b/http_server.c similarity index 71% rename from https_server.c rename to http_server.c index b6ea827..4767386 100644 --- a/https_server.c +++ b/http_server.c @@ -15,6 +15,7 @@ * along with this program. If not, see . */ +#include #include #include #include @@ -23,6 +24,7 @@ #include "freertos/task.h" #include "freertos/event_groups.h" #include "rom/queue.h" +#include "driver/gpio.h" #include "esp_log.h" @@ -31,8 +33,6 @@ #include "lwip/api.h" #include "http_parser.h" -#include "https_server.h" - #include "mbedtls/platform.h" #include "mbedtls/net_sockets.h" #include "mbedtls/esp_debug.h" @@ -41,6 +41,7 @@ #include "mbedtls/ctr_drbg.h" #include "mbedtls/error.h" #include "mbedtls/certs.h" +#include "mbedtls/ssl_ticket.h" #if defined(MBEDTLS_SSL_CACHE_C) #include "mbedtls/ssl_cache.h" @@ -110,10 +111,10 @@ struct http_context_ { }; struct http_server_context_ { - int port; - err_t server_task_err; + int port; TaskHandle_t task; - EventGroupHandle_t start_done; + EventGroupHandle_t start_done; + err_t server_task_err; SLIST_HEAD(, http_handler_t) handlers; _lock_t handlers_lock; struct http_context_ connection_context; @@ -125,13 +126,15 @@ struct http_server_context_ { mbedtls_x509_crt *srvcert; mbedtls_pk_context *pkey; mbedtls_ssl_cache_context *cache; + mbedtls_ssl_ticket_context *ticket_ctx; #else struct netconn* server_conn; #endif }; -#define SERVER_STARTED_BIT BIT(0) -#define SERVER_DONE_BIT BIT(1) +#define SERVER_STARTED_BIT BIT(0) +#define SERVER_DONE_BIT BIT(1) +#define SERVER_ERR_NO_MEM BIT(2) static const char* http_response_code_to_str(int code); @@ -139,24 +142,6 @@ static esp_err_t add_keyval_pair(http_header_list_t *list, const char* name, con static const char* TAG = "http_server"; -const static char index_html[] = "" - "\n" - "\n" - " \n" - " \n" - "HELLO ESP32\n" - "\n" - "\n" - "

Hello World, from ESP32!

\n" - "\n" - "\n"; - -const static char response_OK[] = - "OK!\n"; - esp_err_t http_register_handler(http_server_t server, const char* uri_pattern, int method, @@ -589,6 +574,7 @@ static esp_err_t http_send_response_headers(http_context_t http_ctx) if( ret < 0 && ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE ) { ESP_LOGE(TAG, "ERROR: mbedtls_ssl_write returned %d\n\n", ret ); + break; //FIXME: close connection //goto exit; } @@ -668,6 +654,7 @@ esp_err_t http_response_write(http_context_t http_ctx, const http_buffer_t* buff if( ret < 0 && ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE ) { ESP_LOGE(TAG, "ERROR: mbedtls_ssl_write returned %d\n\n", ret ); + break; //FIXME: close connection //goto exit; } @@ -879,11 +866,11 @@ static void http_handle_connection(http_server_t server, void *arg_conn) if (ret > 0) { ctx->state = HTTP_COLLECTING_RESPONSE_HEADERS; if (ctx->handler == NULL) { - ESP_LOGD(TAG, "No registered Handler!") + ESP_LOGD(TAG, "No registered Handler!"); http_send_not_found_response(ctx); } else { - ESP_LOGD(TAG, "Registered Handler Found!") + ESP_LOGD(TAG, "Registered Handler Found!"); invoke_handler(ctx, HTTP_HANDLE_RESPONSE); } } @@ -891,10 +878,10 @@ static void http_handle_connection(http_server_t server, void *arg_conn) if (err == ERR_OK) { ctx->state = HTTP_COLLECTING_RESPONSE_HEADERS; if (ctx->handler == NULL) { - ESP_LOGD(TAG, "No registered Handler!") + ESP_LOGD(TAG, "No registered Handler!"); http_send_not_found_response(ctx); } else { - ESP_LOGD(TAG, "Registered Handler Found!") + ESP_LOGD(TAG, "Registered Handler Found!"); invoke_handler(ctx, HTTP_HANDLE_RESPONSE); } } @@ -933,274 +920,342 @@ static void http_handle_connection(http_server_t server, void *arg_conn) static void http_server(void *arg) { + uint8_t bits; http_server_t ctx = (http_server_t) arg; -#ifdef HTTPS_SERVER - char *error_buf; - ESP_LOGV(TAG, "Declaring local mbedTLS context on task..."); - int ret; - mbedtls_net_context listen_fd; - mbedtls_entropy_context entropy; - mbedtls_ctr_drbg_context ctr_drbg; - mbedtls_ssl_context ssl_conn; - mbedtls_ssl_config conf; - mbedtls_x509_crt srvcert; - mbedtls_pk_context pkey; -#if defined(MBEDTLS_SSL_CACHE_C) - mbedtls_ssl_cache_context cache; - (ctx->cache) = &cache; -#endif - (ctx->listen_fd) = &listen_fd; - (ctx->entropy) = &entropy; - (ctx->ctr_drbg) = &ctr_drbg; - (ctx->connection_context.ssl_conn) = &ssl_conn; - (ctx->conf) = &conf; - (ctx->srvcert) = &srvcert; - (ctx->pkey) = &pkey; - - ESP_LOGV(TAG, "Reading CA certificate......"); - extern const unsigned char cacert_pem_start[] asm("_binary_cacert_pem_start"); - extern const unsigned char cacert_pem_end[] asm("_binary_cacert_pem_end"); - const unsigned int cacert_pem_bytes = cacert_pem_end - cacert_pem_start; - - ESP_LOGV(TAG, "Reading Private Key......"); - extern const unsigned char prvtkey_pem_start[] asm("_binary_prvtkey_pem_start"); - extern const unsigned char prvtkey_pem_end[] asm("_binary_prvtkey_pem_end"); - const unsigned int prvtkey_pem_bytes = prvtkey_pem_end - prvtkey_pem_start; - - ESP_LOGV(TAG, "Setting mbedTLS context......"); - mbedtls_net_init( ctx->listen_fd ); - ESP_LOGV(TAG, "OK"); - - ESP_LOGV(TAG, "SSL server context create ......"); - mbedtls_ssl_init( ctx->connection_context.ssl_conn ); - ESP_LOGV(TAG, "OK"); - - ESP_LOGV(TAG, "SSL conf context create ......"); - mbedtls_ssl_config_init( ctx->conf ); - ESP_LOGV(TAG, "OK"); + do{ + ESP_LOGV(TAG, "Checking Server Status..."); + bits = xEventGroupWaitBits(ctx->start_done, SERVER_STARTED_BIT | SERVER_DONE_BIT, 0, pdTRUE, 1000 / portTICK_PERIOD_MS); + + //If server had already been successfully started but it has crashed, + if ((bits & SERVER_STARTED_BIT) && (bits & SERVER_DONE_BIT)) { + ESP_LOGE(TAG, "Server has closed. Restarting server..."); + xEventGroupClearBits(ctx->start_done, SERVER_STARTED_BIT | SERVER_DONE_BIT); + memset(&(ctx->connection_context), 0, sizeof(*ctx) - (size_t)((int)&(ctx->connection_context) - (int)ctx) ); + bits = pdFALSE; + } + + //If server has not successfully been started yet, + if (!(bits & SERVER_STARTED_BIT)) { + #ifdef HTTPS_SERVER + char *error_buf; + ESP_LOGV(TAG, "Declaring local mbedTLS context on task..."); + int ret; + mbedtls_net_context listen_fd; + mbedtls_net_context client_fd; + mbedtls_entropy_context entropy; + mbedtls_ctr_drbg_context ctr_drbg; + mbedtls_ssl_context ssl_conn; + mbedtls_ssl_config conf; + mbedtls_x509_crt srvcert; + mbedtls_pk_context pkey; + #if defined(MBEDTLS_SSL_CACHE_C) + mbedtls_ssl_cache_context cache; + (ctx->cache) = &cache; + #endif + #if defined(MBEDTLS_SSL_SESSION_TICKETS) + mbedtls_ssl_ticket_context ticket_ctx; + (ctx->ticket_ctx) = &ticket_ctx; + #endif + (ctx->listen_fd) = &listen_fd; + (ctx->connection_context.client_fd) = &client_fd; + (ctx->entropy) = &entropy; + (ctx->ctr_drbg) = &ctr_drbg; + (ctx->connection_context.ssl_conn) = &ssl_conn; + (ctx->conf) = &conf; + (ctx->srvcert) = &srvcert; + (ctx->pkey) = &pkey; + + ESP_LOGV(TAG, "Reading Root CA certificate......"); + extern const unsigned char rootcacert_pem_start[] asm("_binary_rootCertificate_pem_start"); + extern const unsigned char rootcacert_pem_end[] asm("_binary_rootCertificate_pem_end"); + const unsigned int rootcacert_pem_bytes = rootcacert_pem_end - rootcacert_pem_start; + + /* + ESP_LOGV(TAG, "Reading Intermediate CA certificate......"); + extern const unsigned char intermediatecacert_pem_start[] asm("_binary_intermediatecacert_pem_start"); + extern const unsigned char intermediatecacert_pem_end[] asm("_binary_intermediatecacert_pem_end"); + const unsigned int intermediatecacert_pem_bytes = intermediatecacert_pem_end - intermediatecacert_pem_start; + */ + + ESP_LOGV(TAG, "Reading Server certificate......"); + extern const unsigned char servercert_pem_start[] asm("_binary_esp32Certificate_pem_start"); + extern const unsigned char servercert_pem_end[] asm("_binary_esp32Certificate_pem_end"); + const unsigned int servercert_pem_bytes = servercert_pem_end - servercert_pem_start; + + ESP_LOGV(TAG, "Reading Server Private Key......"); + extern const unsigned char serverprvtkey_pem_start[] asm("_binary_esp32_key_pem_start"); + extern const unsigned char serverprvtkey_pem_end[] asm("_binary_esp32_key_pem_end"); + const unsigned int serverprvtkey_pem_bytes = serverprvtkey_pem_end - serverprvtkey_pem_start; + + ESP_LOGV(TAG, "Setting server_fd......"); + mbedtls_net_init( ctx->listen_fd ); + ESP_LOGV(TAG, "OK"); + + ESP_LOGV(TAG, "Setting client fd......"); + mbedtls_net_init( ctx->connection_context.client_fd ); + ESP_LOGV(TAG, "OK"); + + ESP_LOGV(TAG, "SSL server context create ......"); + mbedtls_ssl_init( ctx->connection_context.ssl_conn ); + ESP_LOGV(TAG, "OK"); + + ESP_LOGV(TAG, "SSL conf context create ......"); + mbedtls_ssl_config_init( ctx->conf ); + ESP_LOGV(TAG, "OK"); #if defined(MBEDTLS_SSL_CACHE_C) - mbedtls_ssl_cache_init( &cache ); + mbedtls_ssl_cache_init( ctx->cache ); #endif - mbedtls_x509_crt_init( ctx->srvcert ); - mbedtls_pk_init( ctx->pkey ); - mbedtls_entropy_init( ctx->entropy ); - mbedtls_ctr_drbg_init( ctx->ctr_drbg ); - - /* - * 1. Load the certificates and private RSA key - */ - ESP_LOGD(TAG, "Loading the server cert. and key..." ); - /* - * This demonstration program uses embedded test certificates. - * Instead, you may want to use mbedtls_x509_crt_parse_file() to read the - * server and CA certificates, as well as mbedtls_pk_parse_keyfile(). - */ - ESP_LOGV(TAG, "SSL server context set own certification......"); - ESP_LOGV(TAG, "Parsing test srv_crt......"); - ret = mbedtls_x509_crt_parse( ctx->srvcert, (const unsigned char *) cacert_pem_start, - cacert_pem_bytes ); - if( ret != ERR_OK ) - { - ESP_LOGE(TAG, "ERROR: mbedtls_x509_crt_parse returned %d", ret ); - goto exit; - } - ESP_LOGV(TAG, "OK"); - - ESP_LOGV(TAG, "SSL server context set private key......"); - ret = mbedtls_pk_parse_key( ctx->pkey, (const unsigned char *) prvtkey_pem_start, - prvtkey_pem_bytes, NULL, 0 ); - if( ret != ERR_OK ) - { - ESP_LOGE(TAG, "ERROR: mbedtls_pk_parse_key returned %d", ret ); - goto exit; - } - ESP_LOGV(TAG, "OK"); +#if defined(MBEDTLS_SSL_SESSION_TICKETS) + mbedtls_ssl_ticket_init( ctx->ticket_ctx ); +#endif + mbedtls_x509_crt_init( ctx->srvcert ); + mbedtls_pk_init( ctx->pkey ); + mbedtls_entropy_init( ctx->entropy ); + mbedtls_ctr_drbg_init( ctx->ctr_drbg ); + + /* + * 1. Load the certificates and private RSA key + */ + ESP_LOGD(TAG, "Loading the server cert. and key..." ); + /* + * This demonstration program uses embedded test certificates. + * Instead, you may want to use mbedtls_x509_crt_parse_file() to read the + * server and CA certificates, as well as mbedtls_pk_parse_keyfile(). + */ + ESP_LOGV(TAG, "SSL server context set own certification......"); + ESP_LOGV(TAG, "Parsing test srv_crt......"); + ret = mbedtls_x509_crt_parse( ctx->srvcert, (const unsigned char *) servercert_pem_start, + servercert_pem_bytes ); + if( ret != ERR_OK ) + { + ESP_LOGE(TAG, "ERROR: mbedtls_x509_crt_parse returned %d", ret ); + goto exit; + } + ESP_LOGV(TAG, "OK"); - /* - * 3. Seed the RNG - */ - ESP_LOGV(TAG, "Seeding the random number generator..." ); - if( ( ret = mbedtls_ctr_drbg_seed( ctx->ctr_drbg, mbedtls_entropy_func, ctx->entropy, - (const unsigned char *) TAG, - strlen( TAG ) ) ) != 0 ) - { - ESP_LOGE(TAG, "ERROR: mbedtls_ctr_drbg_seed returned %d", ret ); - goto exit; - } - ESP_LOGV(TAG, "OK"); + /* + ESP_LOGV(TAG, "Parsing Intermediate CA crt......"); + ret = mbedtls_x509_crt_parse( ctx->srvcert, (const unsigned char *) intermediatecacert_pem_start, + intermediatecacert_pem_bytes ); + if( ret != ERR_OK ) + { + ESP_LOGE(TAG, "ERROR: mbedtls_x509_crt_parse returned %d", ret ); + goto exit; + } + ESP_LOGV(TAG, "OK"); + */ - /* - * 2. Setup the listening TCP socket - */ - char *port = malloc(sizeof(char) * 6); - ESP_LOGV(TAG, "SSL server socket bind at localhost: %s ......", itoa(ctx->port, port,10)); - if( ( ret = mbedtls_net_bind( ctx->listen_fd, NULL, itoa(ctx->port, port,10), MBEDTLS_NET_PROTO_TCP ) ) != 0 ) - { - ESP_LOGE(TAG, "ERROR: mbedtls_net_bind returned %d", ret ); - goto exit; - } - free(port); - ESP_LOGV(TAG, "OK"); + ESP_LOGV(TAG, "Parsing Root CA crt......"); + ret = mbedtls_x509_crt_parse( ctx->srvcert, (const unsigned char *) rootcacert_pem_start, + rootcacert_pem_bytes ); + if( ret != ERR_OK ) + { + ESP_LOGE(TAG, "ERROR: mbedtls_x509_crt_parse returned %d", ret ); + goto exit; + } + ESP_LOGV(TAG, "OK"); - /* - * 4. Setup stuff - */ - ESP_LOGV(TAG, "Setting up the SSL conf data...." ); -#ifdef CONFIG_MBEDTLS_DEBUG - mbedtls_esp_enable_debug_log(ctx->conf, 4); -#endif - if( ( ret = mbedtls_ssl_config_defaults( ctx->conf, - MBEDTLS_SSL_IS_SERVER, - MBEDTLS_SSL_TRANSPORT_STREAM, - MBEDTLS_SSL_PRESET_DEFAULT ) ) != 0 ) - { - ESP_LOGE(TAG, "ERROR: mbedtls_ssl_config_defaults returned %d", ret ); - goto exit; - } + ESP_LOGV(TAG, "SSL server context set private key......"); + ret = mbedtls_pk_parse_key( ctx->pkey, (const unsigned char *) serverprvtkey_pem_start, + serverprvtkey_pem_bytes, NULL, 0 ); + if( ret != ERR_OK ) + { + ESP_LOGE(TAG, "ERROR: mbedtls_pk_parse_key returned %d", ret ); + goto exit; + } + ESP_LOGV(TAG, "OK"); + + /* + * 3. Seed the RNG + */ + ESP_LOGV(TAG, "Seeding the random number generator..." ); + if( ( ret = mbedtls_ctr_drbg_seed( ctx->ctr_drbg, mbedtls_entropy_func, ctx->entropy, + (const unsigned char *) TAG, + strlen( TAG ) ) ) != 0 ) + { + ESP_LOGE(TAG, "ERROR: mbedtls_ctr_drbg_seed returned %d", ret ); + goto exit; + } + ESP_LOGV(TAG, "OK"); + + /* + * 2. Setup the listening TCP socket + */ + char *port = malloc(sizeof(char) * 6); + ESP_LOGV(TAG, "SSL server socket bind at localhost: %s ......", itoa(ctx->port, port,10)); + if( ( ret = mbedtls_net_bind( ctx->listen_fd, NULL, itoa(ctx->port, port,10), MBEDTLS_NET_PROTO_TCP ) ) != 0 ) + { + ESP_LOGE(TAG, "ERROR: mbedtls_net_bind returned %d", ret ); + goto exit; + } + free(port); + ESP_LOGV(TAG, "OK"); + + + /* + * 4. Setup stuff + */ + ESP_LOGV(TAG, "Setting up the SSL conf data...." ); + #ifdef CONFIG_MBEDTLS_DEBUG + mbedtls_esp_enable_debug_log(ctx->conf, 4); + #endif + if( ( ret = mbedtls_ssl_config_defaults( ctx->conf, + MBEDTLS_SSL_IS_SERVER, + MBEDTLS_SSL_TRANSPORT_STREAM, + MBEDTLS_SSL_PRESET_DEFAULT ) ) != 0 ) + { + ESP_LOGE(TAG, "ERROR: mbedtls_ssl_config_defaults returned %d", ret ); + goto exit; + } - mbedtls_ssl_conf_rng( ctx->conf, mbedtls_ctr_drbg_random, ctx->ctr_drbg ); + mbedtls_ssl_conf_rng( ctx->conf, mbedtls_ctr_drbg_random, ctx->ctr_drbg ); #if defined(MBEDTLS_SSL_CACHE_C) - mbedtls_ssl_conf_session_cache( ctx->conf, ctx->cache, - mbedtls_ssl_cache_get, - mbedtls_ssl_cache_set ); + mbedtls_ssl_conf_session_cache( ctx->conf, ctx->cache, + mbedtls_ssl_cache_get, + mbedtls_ssl_cache_set ); #endif - mbedtls_ssl_conf_ca_chain( ctx->conf, (*ctx->srvcert).next, NULL ); - if( ( ret = mbedtls_ssl_conf_own_cert( ctx->conf, ctx->srvcert, ctx->pkey ) ) != 0 ) - { - ESP_LOGE(TAG, "ERROR: mbedtls_ssl_conf_own_cert returned %d", ret ); - goto exit; - } - - if( ( ret = mbedtls_ssl_setup( ctx->connection_context.ssl_conn, ctx->conf ) ) != 0 ) - { - ESP_LOGE(TAG, "ERROR: mbedtls_ssl_setup returned %d", ret ); - goto exit; - } - ESP_LOGV(TAG, "OK"); - - xEventGroupSetBits(ctx->start_done, SERVER_STARTED_BIT); - -reset: - ESP_LOGI(TAG, "mbedTLS HTTPS server is running! Waiting for new connection..."); - do { - mbedtls_net_context client_fd; - (ctx->connection_context.client_fd) = &client_fd; - mbedtls_net_init( ctx->connection_context.client_fd ); + ESP_LOGV(TAG, "Setting up the SSL Session Tickets...." ); +#if defined(MBEDTLS_SSL_SESSION_TICKETS) + if( ( ret = mbedtls_ssl_ticket_setup( ctx->ticket_ctx , + mbedtls_ctr_drbg_random, &ctr_drbg, + MBEDTLS_CIPHER_AES_256_GCM, + 86400 ) ) != 0 ) + { + ESP_LOGE(TAG, "ERROR: mbedtls_ssl_ticket_setup returned %d", ret ); + goto exit; + } -#ifdef MBEDTLS_ERROR_C - if( ret != ERR_OK ) - { - error_buf = malloc(sizeof(char)*ERROR_BUF_LENGTH); - mbedtls_strerror( ctx->server_task_err, error_buf, sizeof(char)*ERROR_BUF_LENGTH ); - ESP_LOGE(TAG, "Error %d: %s", ret, error_buf ); - free(error_buf); - } + mbedtls_ssl_conf_session_tickets_cb( &conf, + mbedtls_ssl_ticket_write, + mbedtls_ssl_ticket_parse, + ctx->ticket_ctx ); #endif - mbedtls_net_free( ctx->connection_context.client_fd ); - - mbedtls_ssl_session_reset( ctx->connection_context.ssl_conn ); - /* - * 3. Wait until a client connects - */ - ESP_LOGV(TAG, "Wait until a client connects..." ); - if( ( ret = mbedtls_net_accept( ctx->listen_fd, ctx->connection_context.client_fd, - NULL, 0, NULL ) ) != 0 ) - { - ESP_LOGE(TAG, "ERROR: mbedtls_net_accept returned %d", ret ); - goto exit; - } - mbedtls_ssl_set_bio( ctx->connection_context.ssl_conn, ctx->connection_context.client_fd, mbedtls_net_send, mbedtls_net_recv, NULL ); - ESP_LOGV(TAG, "OK"); - - /* - * 5. Handshake - */ - ESP_LOGV(TAG, "Performing the SSL/TLS handshake..." ); - while( ( ret = mbedtls_ssl_handshake( ctx->connection_context.ssl_conn ) ) != ERR_OK ) - { - if( ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE ) + mbedtls_ssl_conf_ca_chain( ctx->conf, (*ctx->srvcert).next, NULL ); + if( ( ret = mbedtls_ssl_conf_own_cert( ctx->conf, ctx->srvcert, ctx->pkey ) ) != 0 ) { - ESP_LOGE(TAG, "ERROR: bedtls_ssl_handshake returned %d", ret ); - goto reset; + ESP_LOGE(TAG, "ERROR: mbedtls_ssl_conf_own_cert returned %d", ret ); + goto exit; } - } - ESP_LOGV(TAG, "OK"); - ESP_LOGV(TAG, "Handling connection..." ); - if (ret == ERR_OK) { - http_handle_connection(ctx, NULL); - } - ESP_LOGV(TAG, "OK"); - } while (ret == ERR_OK); - -exit: -if (ret != ERR_OK) { - error_buf = malloc(sizeof(char)*ERROR_BUF_LENGTH); - mbedtls_strerror( ctx->server_task_err, error_buf, sizeof(char)*ERROR_BUF_LENGTH ); - ESP_LOGE(TAG, "Error %d: %s", ret, error_buf ); - free(error_buf); - ctx->server_task_err = ret; - xEventGroupSetBits(ctx->start_done, SERVER_DONE_BIT); -} + if( ( ret = mbedtls_ssl_setup( ctx->connection_context.ssl_conn, ctx->conf ) ) != 0 ) + { + ESP_LOGE(TAG, "ERROR: mbedtls_ssl_setup returned %d", ret ); + goto exit; + } + ESP_LOGV(TAG, "OK"); + + xEventGroupSetBits(ctx->start_done, SERVER_STARTED_BIT); + reset: + ESP_LOGI(TAG, "mbedTLS HTTPS server is running! Waiting for new connection..."); + do { + mbedtls_net_free( ctx->connection_context.client_fd ); + + mbedtls_ssl_session_reset( ctx->connection_context.ssl_conn ); + /* + * 3. Wait until a client connects + */ + ESP_LOGV(TAG, "Wait until a client connects..." ); + if( ( ret = mbedtls_net_accept( ctx->listen_fd, ctx->connection_context.client_fd, + NULL, 0, NULL ) ) != 0 ) + { + ESP_LOGE(TAG, "ERROR: mbedtls_net_accept returned %d", ret ); + goto exit; + } + mbedtls_ssl_set_bio( ctx->connection_context.ssl_conn, ctx->connection_context.client_fd, mbedtls_net_send, mbedtls_net_recv, NULL ); + ESP_LOGV(TAG, "OK"); + + /* + * 5. Handshake + */ + ESP_LOGV(TAG, "Performing the SSL/TLS handshake..." ); + while( ( ret = mbedtls_ssl_handshake( ctx->connection_context.ssl_conn ) ) != ERR_OK ) + { + if( ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE ) + { + ESP_LOGE(TAG, "ERROR: bedtls_ssl_handshake returned %d", ret ); + goto reset; + } + } + ESP_LOGV(TAG, "OK"); + ESP_LOGV(TAG, "Handling connection..." ); + if (ret == ERR_OK) { + http_handle_connection(ctx, NULL); + } + ESP_LOGV(TAG, "OK"); + } while (ret == ERR_OK); + + exit: + if (ret != ERR_OK) { + error_buf = malloc(sizeof(char)*ERROR_BUF_LENGTH); + mbedtls_strerror( ret, error_buf, sizeof(char)*ERROR_BUF_LENGTH ); + ESP_LOGE(TAG, "Error %d: %s", ret, error_buf ); + free(error_buf); + + //Set SERVER_DONE_BIT and save error at http_server_t struct + ctx->server_task_err = ret; + xEventGroupSetBits(ctx->start_done, SERVER_DONE_BIT); + } - mbedtls_net_free( ctx->connection_context.client_fd ); - mbedtls_net_free( ctx->listen_fd ); - mbedtls_x509_crt_free( ctx->srvcert ); - mbedtls_pk_free( ctx->pkey ); - mbedtls_ssl_free( ctx->connection_context.ssl_conn ); - mbedtls_ssl_config_free( ctx->conf ); + mbedtls_net_free( ctx->connection_context.client_fd ); + mbedtls_net_free( ctx->listen_fd ); + mbedtls_x509_crt_free( ctx->srvcert ); + mbedtls_pk_free( ctx->pkey ); + mbedtls_ssl_free( ctx->connection_context.ssl_conn ); + mbedtls_ssl_config_free( ctx->conf ); #if defined(MBEDTLS_SSL_CACHE_C) - mbedtls_ssl_cache_free( ctx->cache ); + mbedtls_ssl_cache_free( ctx->cache ); #endif - mbedtls_ctr_drbg_free( ctx->ctr_drbg ); - mbedtls_entropy_free( ctx->entropy ); - - ESP_LOGE(TAG, "Closing Task"); - vTaskDelete(NULL); - -#else - struct netconn *client_conn; - err_t err; - ctx->server_conn = netconn_new(NETCONN_TCP); - if (ctx->server_conn == NULL) { - err = ERR_MEM; - goto out; - } - - err = netconn_bind(ctx->server_conn, NULL, ctx->port); - if (err != ERR_OK) { - goto out; - } +#if defined(MBEDTLS_SSL_SESSION_TICKETS) + mbedtls_ssl_ticket_free( ctx->ticket_ctx ); +#endif + mbedtls_ctr_drbg_free( ctx->ctr_drbg ); + mbedtls_entropy_free( ctx->entropy ); + + #else + struct netconn *client_conn; + err_t err; + ctx->server_conn = netconn_new(NETCONN_TCP); + if (ctx->server_conn == NULL) { + err = ERR_MEM; + goto out; + } - err = netconn_listen(ctx->server_conn); - if (err != ERR_OK) { - goto out; - } - xEventGroupSetBits(ctx->start_done, SERVER_STARTED_BIT); + err = netconn_bind(ctx->server_conn, NULL, ctx->port); + if (err != ERR_OK) { + goto out; + } - do { - err = netconn_accept(ctx->server_conn, &client_conn); - if (err == ERR_OK) { - http_handle_connection(ctx, client_conn); - netconn_delete(client_conn); - } - } while (err == ERR_OK); -out: - if (ctx->server_conn) { - netconn_close(ctx->server_conn); - netconn_delete(ctx->server_conn); - } - if (err != ERR_OK) { - ctx->server_task_err = err; - xEventGroupSetBits(ctx->start_done, SERVER_DONE_BIT); - } - vTaskDelete(NULL); -#endif + err = netconn_listen(ctx->server_conn); + if (err != ERR_OK) { + goto out; + } + xEventGroupSetBits(ctx->start_done, SERVER_STARTED_BIT); + + do { + err = netconn_accept(ctx->server_conn, &client_conn); + if (err == ERR_OK) { + http_handle_connection(ctx, client_conn); + netconn_delete(client_conn); + } + } while (err == ERR_OK); + out: + if (ctx->server_conn) { + netconn_close(ctx->server_conn); + netconn_delete(ctx->server_conn); + } + if (err != ERR_OK) { + ctx->server_task_err = err; + xEventGroupSetBits(ctx->start_done, SERVER_DONE_BIT); + } + vTaskDelete(NULL); + #endif + } + }while(1); } esp_err_t http_server_start(const http_server_options_t* options, http_server_t* out_server) @@ -1217,38 +1272,26 @@ esp_err_t http_server_start(const http_server_options_t* options, http_server_t* return ESP_ERR_NO_MEM; } + //Start http_server task if it had not been started before ESP_LOGV(TAG, "Creating http_server task..."); - int ret = xTaskCreatePinnedToCore(&http_server, "httpd", - options->task_stack_size, ctx, - options->task_priority, - &ctx->task, - options->task_affinity); - if (ret != pdPASS) { - vEventGroupDelete(ctx->start_done); - free(ctx); - return ESP_ERR_NO_MEM; - } - ESP_LOGI(TAG, "Task has been created!"); + int ret = xTaskCreatePinnedToCore(&http_server, "http_server", + options->task_stack_size, ctx, + options->task_priority, + &ctx->task, + options->task_affinity); + if (ret != pdPASS) { + vEventGroupDelete(ctx->start_done); + free(ctx); + return ESP_ERR_NO_MEM; + } + ESP_LOGI(TAG, "http_server task has been created!"); + //Check server status by checking SERVER_STARTED_BIT (it server has been succesfully started) or SERVER_DONE_BIT (if it has crashed) ESP_LOGV(TAG, "Checking server status..."); - int bits = xEventGroupWaitBits(ctx->start_done, SERVER_STARTED_BIT | SERVER_DONE_BIT, 0, 0, portMAX_DELAY); - if (bits & SERVER_DONE_BIT) { - ESP_LOGE(TAG, "SERVER_DONE_BIT Error..."); -#ifdef HTTPS_SERVER - char *error_buf = malloc(sizeof(char)*ERROR_BUF_LENGTH); - mbedtls_strerror( ctx->server_task_err, error_buf, 100 ); - ESP_LOGE(TAG, "Error %d: %s", ret, error_buf ); - free(error_buf); -#endif - esp_err_t err = lwip_err_to_esp_err(ctx->server_task_err); - vEventGroupDelete(ctx->start_done); - free(ctx); - return err; - } - + xEventGroupWaitBits(ctx->start_done, SERVER_STARTED_BIT, 0, 0, portMAX_DELAY); ESP_LOGI(TAG, "Server started!"); - *out_server = ctx; - return ESP_OK; + *out_server = ctx; + return ESP_OK; } esp_err_t http_server_stop(http_server_t server) diff --git a/https_server.h b/http_server.h similarity index 95% rename from https_server.h rename to http_server.h index f2a2216..904eafc 100644 --- a/https_server.h +++ b/http_server.h @@ -28,6 +28,7 @@ extern "C" { */ #include "sdkconfig.h" +#include "esp_system.h" /* Pull in the definitions of HTTP methods */ #include "http_parser.h" @@ -81,6 +82,24 @@ typedef struct { .task_priority = 8, \ } +const static char index_html[] = "" + "\n" + "\n" + " \n" + " \n" + "HELLO ESP32\n" + "\n" + "\n" + "

Hello World, from ESP32!

\n" + "\n" + "\n"; + +const static char response_OK[] = + "OK!\n"; + /** * @brief initialize HTTP server, start listening * @param options pointer to http server options, can point to a temporary From 69a14e935b312cf2e9c80a1dfc0b5252b9f04b27 Mon Sep 17 00:00:00 2001 From: Tiago Medicci Date: Fri, 15 Jun 2018 15:59:25 -0300 Subject: [PATCH 09/10] Added function to check if HTTP server is currently handling a request --- http_server.c | 46 +++++++++++++++++++++++++++++++++++++--------- http_server.h | 17 +++++++++++++++++ 2 files changed, 54 insertions(+), 9 deletions(-) diff --git a/http_server.c b/http_server.c index 4767386..9707e42 100644 --- a/http_server.c +++ b/http_server.c @@ -132,11 +132,6 @@ struct http_server_context_ { #endif }; -#define SERVER_STARTED_BIT BIT(0) -#define SERVER_DONE_BIT BIT(1) -#define SERVER_ERR_NO_MEM BIT(2) - - static const char* http_response_code_to_str(int code); static esp_err_t add_keyval_pair(http_header_list_t *list, const char* name, const char* val); @@ -1150,7 +1145,9 @@ static void http_server(void *arg) ESP_LOGV(TAG, "OK"); xEventGroupSetBits(ctx->start_done, SERVER_STARTED_BIT); - reset: +reset: + ESP_LOGI(TAG, "Clearing SERVER_PROCESSING_REQUEST bit..."); + xEventGroupClearBits(ctx->start_done, SERVER_PROCESSING_REQUEST); ESP_LOGI(TAG, "mbedTLS HTTPS server is running! Waiting for new connection..."); do { mbedtls_net_free( ctx->connection_context.client_fd ); @@ -1166,6 +1163,8 @@ static void http_server(void *arg) ESP_LOGE(TAG, "ERROR: mbedtls_net_accept returned %d", ret ); goto exit; } + ESP_LOGI(TAG, "Setting SERVER_PROCESSING_REQUEST bit..."); + xEventGroupSetBits(ctx->start_done, SERVER_PROCESSING_REQUEST); mbedtls_ssl_set_bio( ctx->connection_context.ssl_conn, ctx->connection_context.client_fd, mbedtls_net_send, mbedtls_net_recv, NULL ); ESP_LOGV(TAG, "OK"); @@ -1186,10 +1185,12 @@ static void http_server(void *arg) if (ret == ERR_OK) { http_handle_connection(ctx, NULL); } + ESP_LOGI(TAG, "Clearing SERVER_PROCESSING_REQUEST bit..."); + xEventGroupClearBits(ctx->start_done, SERVER_PROCESSING_REQUEST); ESP_LOGV(TAG, "OK"); } while (ret == ERR_OK); - exit: +exit: if (ret != ERR_OK) { error_buf = malloc(sizeof(char)*ERROR_BUF_LENGTH); mbedtls_strerror( ret, error_buf, sizeof(char)*ERROR_BUF_LENGTH ); @@ -1199,6 +1200,8 @@ static void http_server(void *arg) //Set SERVER_DONE_BIT and save error at http_server_t struct ctx->server_task_err = ret; xEventGroupSetBits(ctx->start_done, SERVER_DONE_BIT); + ESP_LOGI(TAG, "Clearing SERVER_PROCESSING_REQUEST bit..."); + xEventGroupClearBits(ctx->start_done, SERVER_PROCESSING_REQUEST); } mbedtls_net_free( ctx->connection_context.client_fd ); @@ -1215,8 +1218,7 @@ static void http_server(void *arg) #endif mbedtls_ctr_drbg_free( ctx->ctr_drbg ); mbedtls_entropy_free( ctx->entropy ); - - #else +#else struct netconn *client_conn; err_t err; ctx->server_conn = netconn_new(NETCONN_TCP); @@ -1239,8 +1241,12 @@ static void http_server(void *arg) do { err = netconn_accept(ctx->server_conn, &client_conn); if (err == ERR_OK) { + ESP_LOGI(TAG, "Setting SERVER_PROCESSING_REQUEST bit..."); + xEventGroupSetBits(ctx->start_done, SERVER_PROCESSING_REQUEST); http_handle_connection(ctx, client_conn); netconn_delete(client_conn); + ESP_LOGI(TAG, "Clearing SERVER_PROCESSING_REQUEST bit..."); + xEventGroupClearBits(ctx->start_done, SERVER_PROCESSING_REQUEST); } } while (err == ERR_OK); out: @@ -1250,6 +1256,8 @@ static void http_server(void *arg) } if (err != ERR_OK) { ctx->server_task_err = err; + ESP_LOGI(TAG, "Clearing SERVER_PROCESSING_REQUEST bit..."); + xEventGroupClearBits(ctx->start_done, SERVER_PROCESSING_REQUEST); xEventGroupSetBits(ctx->start_done, SERVER_DONE_BIT); } vTaskDelete(NULL); @@ -1386,3 +1394,23 @@ esp_err_t simple_POST_method_example(void) return res; } + +/** + * @brief Check if a request is being attended and returns it + * + * @param Current HTTP(S) server context + * + * @return a uint8_t variable indicating if server is processing any request + */ +uint8_t check_processing_request(http_server_t server) +{ + if(xEventGroupWaitBits(server->start_done, SERVER_PROCESSING_REQUEST, 0, 0, 0) & SERVER_PROCESSING_REQUEST) //If a request was not finished properly, returns an error + { + ESP_LOGI(TAG, "Processing Server Request"); + return true; + }else + { + ESP_LOGI(TAG, "No Server Request"); + return false; + } +} diff --git a/http_server.h b/http_server.h index 904eafc..6061f18 100644 --- a/http_server.h +++ b/http_server.h @@ -41,6 +41,14 @@ extern "C" { #define HTTP_HANDLE_DATA BIT(2) /*!< Called each time a fragment of request body is received */ #define HTTP_HANDLE_RESPONSE BIT(3) /*!< Called at the end of the request to produce the response */ +/** + * Bit masks for server event handler + */ +#define SERVER_STARTED_BIT BIT(0) +#define SERVER_DONE_BIT BIT(1) +#define SERVER_ERR_NO_MEM BIT(2) +#define SERVER_PROCESSING_REQUEST BIT(3) + /** Error buffer length */ #define ERROR_BUF_LENGTH 100 @@ -338,6 +346,15 @@ esp_err_t simple_GET_method_example(void); */ esp_err_t simple_POST_method_example(void); +/** + * @brief Check if a request is being attended and returns it + * + * @param Current HTTP(S) server context + * + * @return a uint8_t variable indicating if server is processing any request + */ +uint8_t check_processing_request(http_server_t server); + #ifdef __cplusplus } #endif From d2ebf3176c1d9e3347b5bfe5dbc3b1ee89fe5b52 Mon Sep 17 00:00:00 2001 From: Tiago Medicci Date: Tue, 10 Jul 2018 16:00:34 -0300 Subject: [PATCH 10/10] Added double buffer support while reading HTTP request: if size of data is bigger than MBEDTLS_EXAMPLE_RECV_BUF_LEN, allocate enough space to read all the receiving data --- http_server.c | 128 ++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 123 insertions(+), 5 deletions(-) diff --git a/http_server.c b/http_server.c index 9707e42..0aa7349 100644 --- a/http_server.c +++ b/http_server.c @@ -132,6 +132,23 @@ struct http_server_context_ { #endif }; +/** Return true if \p ret is a status code indicating that there is an + * operation in progress on an SSL connection, and false if it indicates + * success or a fatal error. + * + * The possible operations in progress are: + * + * - A read, when the SSL input buffer does not contain a full message. + * - A write, when the SSL output buffer contains some data that has not + * been sent over the network yet. + * - An asynchronous callback that has not completed yet. */ +static int mbedtls_status_is_ssl_in_progress( int ret ) +{ + return( ret == MBEDTLS_ERR_SSL_WANT_READ || + ret == MBEDTLS_ERR_SSL_WANT_WRITE ); +} + + static const char* http_response_code_to_str(int code); static esp_err_t add_keyval_pair(http_header_list_t *list, const char* name, const char* val); @@ -768,6 +785,9 @@ static const char* http_response_code_to_str(int code) static void http_handle_connection(http_server_t server, void *arg_conn) { unsigned char *buf; + unsigned char *larger_buf; + size_t len = 0; + /* Single threaded server, one context only */ http_context_t ctx = &server->connection_context; @@ -801,9 +821,11 @@ static void http_handle_connection(http_server_t server, void *arg_conn) */ ret = 0; + buf = malloc(sizeof(char)*MBEDTLS_EXAMPLE_RECV_BUF_LEN); + while (ctx->state != HTTP_REQUEST_DONE) { ESP_LOGV(TAG, "Reading from client..." ); - buf = malloc(sizeof(char)*MBEDTLS_EXAMPLE_RECV_BUF_LEN); +/* memset( buf, 0, sizeof(char)*MBEDTLS_EXAMPLE_RECV_BUF_LEN); //FIXME: add support for buffer > MBEDTLS_EXAMPLE_RECV_BUF_LEN ret = mbedtls_ssl_read( server->connection_context.ssl_conn, buf, MBEDTLS_EXAMPLE_RECV_BUF_LEN); @@ -830,13 +852,107 @@ static void http_handle_connection(http_server_t server, void *arg_conn) break; } +*/ + do + { + int terminated = 0; + len = sizeof(char)*MBEDTLS_EXAMPLE_RECV_BUF_LEN; + memset( buf, 0, sizeof(char)*MBEDTLS_EXAMPLE_RECV_BUF_LEN); + ret = mbedtls_ssl_read( server->connection_context.ssl_conn, buf, len ); + + if( mbedtls_status_is_ssl_in_progress( ret ) ) + { + continue; + } + + if( ret <= 0 ) + { + switch( ret ) + { + case MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY: + ESP_LOGW(TAG, "Error: connection was closed gracefully" ); + break; + + case MBEDTLS_ERR_NET_CONN_RESET: + ESP_LOGW(TAG, "Error: connection was reset by peer" ); + break; + + default: + ESP_LOGW(TAG, "Error: mbedtls_ssl_read returned -0x%x\n", -ret ); + break; + } + } + + if( mbedtls_ssl_get_bytes_avail( server->connection_context.ssl_conn ) == 0 ) + { + len = ret; + buf[len] = '\0'; + ESP_LOGI(TAG, "%d bytes read: \n%s", len, (char *) buf ); + /* End of message should be detected according to the syntax of the + * application protocol (eg HTTP), just use a dummy test here. */ + if( buf[len - 1] == '\n' ) + terminated = 1; + } + else + { + size_t extra_len, ori_len; - ESP_LOGD(TAG, "%d bytes read: \n%s", ret, (char *) buf ); + ori_len = ret; + extra_len = (size_t) mbedtls_ssl_get_bytes_avail( server->connection_context.ssl_conn ); + + larger_buf = mbedtls_calloc( 1, ori_len + extra_len + 1 ); + if( larger_buf == NULL ) + { + ESP_LOGE(TAG, "Failed to allocate larger_buf"); + len = 0; + break; + } + + memset( larger_buf, 0, ori_len + extra_len ); + memcpy( larger_buf, buf, ori_len ); + + if(buf) //Free original buffer; + free(buf); + + /* This read should never fail and get the whole cached data */ + ret = mbedtls_ssl_read( server->connection_context.ssl_conn, larger_buf + ori_len, extra_len ); + if( ret != extra_len || + mbedtls_ssl_get_bytes_avail( server->connection_context.ssl_conn ) != 0 ) + { + ESP_LOGE(TAG, "Failed on cached data"); + len = 0; + break; + } + + larger_buf[ori_len + extra_len] = '\0'; + + /* End of message should be detected according to the syntax of the + * application protocol (eg HTTP), just use a dummy test here. */ + if( larger_buf[ori_len + extra_len - 1] == '\n' ) + { + terminated = 1; + len = ori_len + extra_len; + buf = larger_buf; + } + + ESP_LOGI(TAG, "%d bytes read (%d + %d): \n%s", len, + ori_len, extra_len, (char *) buf ); + } + + if( terminated ) + { + break; + } + } + while( 1 ); + + if( len == 0 ) //If output buffer length is 0 (indicating a error), finishes processing of this request + break; ESP_LOGI(TAG, "Calling http_parser_execute..."); - parsed_bytes = http_parser_execute(&ctx->parser, &parser_settings, (char *)buf, ret); + parsed_bytes = http_parser_execute(&ctx->parser, &parser_settings, (char *)buf, len); } - ESP_LOGD(TAG, "Read looping return: %d", parsed_bytes); + ESP_LOGD(TAG, "Read looping returned: %d", parsed_bytes); #else //HTPPS SERVER OFF while (ctx->state != HTTP_REQUEST_DONE) { @@ -858,7 +974,7 @@ static void http_handle_connection(http_server_t server, void *arg_conn) #endif #ifdef HTTPS_SERVER - if (ret > 0) { + if (len > 0) { ctx->state = HTTP_COLLECTING_RESPONSE_HEADERS; if (ctx->handler == NULL) { ESP_LOGD(TAG, "No registered Handler!"); @@ -902,6 +1018,8 @@ static void http_handle_connection(http_server_t server, void *arg_conn) } } ESP_LOGI(TAG, "OK"); + if(buf) + free(buf); #else if (err != ERR_CLSD) { netconn_close(ctx->conn);