/* Simple HTTP server for ESP32. * Copyright Ivan Grokhotkov, 2017. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "freertos/event_groups.h" #include "rom/queue.h" #include "esp_log.h" #include "lwip/sys.h" #include "lwip/netdb.h" #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" #include "mbedtls/ssl.h" #include "mbedtls/entropy.h" #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" #endif #define HTTP_PARSE_BUF_MAX_LEN 256 #define MBEDTLS_EXAMPLE_RECV_BUF_LEN 1024 #define HTTPD_EXAMPLE CONFIG_HTTPD_EXAMPLE typedef enum { HTTP_PARSING_URI, //!< HTTP_PARSING_URI HTTP_PARSING_HEADER_NAME, //!< HTTP_PARSING_HEADER_NAME HTTP_PARSING_HEADER_VALUE, //!< HTTP_PARSING_HEADER_VALUE HTTP_PARSING_REQUEST_BODY, //!< HTTP_PARSING_REQUEST_BODY HTTP_REQUEST_DONE, //!< HTTP_REQUEST_DONE HTTP_COLLECTING_RESPONSE_HEADERS,//!< HTTP_COLLECTING_RESPONSE_HEADERS HTTP_SENDING_RESPONSE_BODY, //!< HTTP_SENDING_RESPONSE_BODY HTTP_DONE, //!< HTTP_DONE } http_state_t; typedef struct http_header_t { char* name; char* value; SLIST_ENTRY(http_header_t) list_entry; } http_header_t; typedef SLIST_HEAD(http_header_list_t, http_header_t) http_header_list_t; typedef struct { http_handler_fn_t cb; void* ctx; } http_form_handler_t; typedef struct http_handler_t{ char* uri_pattern; int method; int events; http_handler_fn_t cb; void* ctx; SLIST_ENTRY(http_handler_t) list_entry; } http_handler_t; struct http_context_ { http_server_t server; http_state_t state; int event; char* uri; char parse_buffer[HTTP_PARSE_BUF_MAX_LEN]; char* request_header_tmp; http_parser parser; http_header_list_t request_headers; int response_code; http_header_list_t response_headers; size_t expected_response_size; size_t accumulated_response_size; http_handler_t* handler; const char* data_ptr; size_t data_size; http_header_list_t request_args; #if HTTPS_SERVER mbedtls_ssl_context *ssl_conn; mbedtls_net_context *client_fd; #else struct netconn *conn; #endif }; struct http_server_context_ { int port; TaskHandle_t task; EventGroupHandle_t start_done; err_t server_task_err; SLIST_HEAD(, http_handler_t) handlers; _lock_t handlers_lock; struct http_context_ connection_context; #if 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; 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_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); static const char* TAG = "http_server"; 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) { http_handler_t* new_handler = (http_handler_t*) calloc(1, sizeof(*new_handler)); if (new_handler == NULL) { return ESP_ERR_NO_MEM; } new_handler->uri_pattern = strdup(uri_pattern); new_handler->cb = callback; new_handler->ctx = callback_arg; new_handler->method = method; new_handler->events = events; _lock_acquire(&server->handlers_lock); /* FIXME: Handlers will be checked in the reverse order */ SLIST_INSERT_HEAD(&server->handlers, new_handler, list_entry); _lock_release(&server->handlers_lock); return ESP_OK; } static http_handler_t* http_find_handler(http_server_t server, const char* uri, int method) { http_handler_t* it; _lock_acquire(&server->handlers_lock); SLIST_FOREACH(it, &server->handlers, list_entry) { if (strcasecmp(uri, it->uri_pattern) == 0 && method == it->method) { break; } } _lock_release(&server->handlers_lock); return it; } static int append_parse_buffer(http_context_t ctx, const char* at, size_t length) { if (length > HTTP_PARSE_BUF_MAX_LEN - strlen(ctx->parse_buffer) - 1) { ESP_LOGW(TAG, "%s: len=%d > %d", __func__, length, HTTP_PARSE_BUF_MAX_LEN - strlen(ctx->parse_buffer) - 1); return 1; } strncat(ctx->parse_buffer, at, length); ESP_LOGV(TAG, "%s: len=%d, '%s'", __func__, length, ctx->parse_buffer); return 0; } static void clear_parse_buffer(http_context_t ctx) { #ifdef NDEBUG ctx->parse_buffer[0] = 0; #else memset(ctx->parse_buffer, 0, sizeof(ctx->parse_buffer)); #endif } static void header_name_done(http_context_t ctx) { ctx->request_header_tmp = strdup(ctx->parse_buffer); clear_parse_buffer(ctx); } static void header_value_done(http_context_t ctx) { const char* value = ctx->parse_buffer; const char* name = ctx->request_header_tmp; 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; clear_parse_buffer(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); } static bool invoke_handler(http_context_t ctx, int event) { if (ctx->handler && (ctx->handler->events & event) != 0) { ctx->event = event; (*ctx->handler->cb)(ctx, ctx->handler->ctx); ctx->event = 0; return true; } return false; } 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); } invoke_handler(ctx, HTTP_HANDLE_HEADERS); ctx->state = HTTP_PARSING_REQUEST_BODY; return 0; } static int parse_hex_digit(char hex) { switch (hex) { case '0' ... '9': return hex - '0'; case 'a' ... 'f': return hex - 'a' + 0xa; case 'A' ... 'F': return hex - 'A' + 0xA; default: return -1; } } static char* urldecode(const char* str, size_t len) { ESP_LOGV(TAG, "urldecode: '%.*s'", len, str); const char* end = str + len; char* out = calloc(1, len + 1); char* p_out = out; while (str != end) { char c = *str++; if (c != '%') { *p_out = c; } else { if (str + 2 > end) { /* Unexpected end of string */ return NULL; } int high = parse_hex_digit(*str++); int low = parse_hex_digit(*str++); if (high == -1 || low == -1) { /* Unexpected character */ return NULL; } *p_out = high * 16 + low; } ++p_out; } *p_out = 0; ESP_LOGV(TAG, "urldecode result: '%s'", out); return out; } static void parse_urlencoded_args(http_context_t ctx, const char* str, size_t len) { const char* end = str + len; const int READING_KEY = 1; const int READING_VAL = 2; int state = READING_KEY; const char* token_start = str; char* key = NULL; char* value = NULL; for (const char* pos = str; pos < end; ++pos) { char c = *pos; if (c == '=' && state == READING_KEY) { key = urldecode(token_start, pos - token_start); state = READING_VAL; token_start = pos + 1; } else if (c == '&' && state == READING_VAL) { value = urldecode(token_start, pos - token_start); state = READING_KEY; token_start = pos + 1; ESP_LOGI(TAG, "Got request argument, '%s': '%s'", key, value); add_keyval_pair(&ctx->request_args, key, value); free(key); key = NULL; free(value); value = NULL; } } if (state == READING_VAL) { value = urldecode(token_start, end - token_start); ESP_LOGI(TAG, "Got request argument, '%s': '%s'", key, value); add_keyval_pair(&ctx->request_args, key, value); free(key); key = NULL; free(value); value = NULL; } } static void uri_done(http_context_t ctx) { /* Check for query argument string */ char* query_str = strchr(ctx->parse_buffer, '?'); if (query_str != NULL) { *query_str = 0; ++query_str; } ctx->uri = strdup(ctx->parse_buffer); ESP_LOGI(TAG, "Got URI: '%s'", ctx->uri); if (query_str) { parse_urlencoded_args(ctx, query_str, strlen(query_str)); } ctx->handler = http_find_handler(ctx->server, ctx->uri, (int) ctx->parser.method); invoke_handler(ctx, HTTP_HANDLE_URI); clear_parse_buffer(ctx); } static int http_header_name_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; if (ctx->state == HTTP_PARSING_URI) { uri_done(ctx); ctx->state = HTTP_PARSING_HEADER_NAME; } else if (ctx->state == HTTP_PARSING_HEADER_VALUE) { header_value_done(ctx); ctx->state = HTTP_PARSING_HEADER_NAME; } return append_parse_buffer(ctx, at, length); } static int http_header_value_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; if (ctx->state == HTTP_PARSING_HEADER_NAME) { header_name_done(ctx); ctx->state = HTTP_PARSING_HEADER_VALUE; } return append_parse_buffer(ctx, at, length); } static int http_body_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; ctx->data_ptr = at; ctx->data_size = length; invoke_handler(ctx, HTTP_HANDLE_DATA); ctx->data_ptr = NULL; ctx->data_size = 0; return 0; } static int http_message_done_cb(http_parser* parser) { ESP_LOGD(TAG, "Called %s", __func__); http_context_t ctx = (http_context_t) parser->data; ctx->state = HTTP_REQUEST_DONE; return 0; } const char* http_request_get_header(http_context_t ctx, const char* name) { http_header_t* it; SLIST_FOREACH(it, &ctx->request_headers, list_entry) { if (strcasecmp(name, it->name) == 0) { return it->value; } } return NULL; } int http_request_get_event(http_context_t ctx) { return ctx->event; } const char* http_request_get_uri(http_context_t ctx) { return ctx->uri; } int http_request_get_method(http_context_t ctx) { return (int) ctx->parser.method; } 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_LOGI(TAG, "Key %s: %s", it->name, it->value); if (strcasecmp(name, it->name) == 0) { return it->value; } } return NULL; } esp_err_t http_request_get_data(http_context_t ctx, const char** out_data_ptr, size_t* out_size) { if (ctx->event != HTTP_HANDLE_DATA) { return ESP_ERR_INVALID_STATE; } *out_data_ptr = ctx->data_ptr; *out_size = ctx->data_size; return ESP_OK; } static void form_data_handler_cb(http_context_t http_ctx, void* ctx) { http_form_handler_t* form_ctx = (http_form_handler_t*) ctx; int event = http_request_get_event(http_ctx); if (event != HTTP_HANDLE_DATA) { (*form_ctx->cb)(http_ctx, form_ctx->ctx); } else { const char* str; size_t len; http_request_get_data(http_ctx, &str, &len); parse_urlencoded_args(http_ctx, str, len); } } esp_err_t http_register_form_handler(http_server_t server, const char* uri_pattern, int method, int events, http_handler_fn_t callback, void* callback_arg) { http_form_handler_t* inner_handler = calloc(1, sizeof(*inner_handler)); if (inner_handler == NULL) { return ESP_ERR_NO_MEM; } inner_handler->cb = callback; inner_handler->ctx = callback_arg; esp_err_t res = http_register_handler(server, uri_pattern, method, events | HTTP_HANDLE_DATA, &form_data_handler_cb, inner_handler); if (res != ESP_OK) { free(inner_handler); } return res; } static esp_err_t lwip_err_to_esp_err(err_t e) { switch (e) { case ERR_OK: return ESP_OK; case ERR_MEM: return ESP_ERR_NO_MEM; case ERR_TIMEOUT: return ESP_ERR_TIMEOUT; default: return ESP_FAIL; } } static void headers_list_clear(http_header_list_t* list) { http_header_t *it, *next; SLIST_FOREACH_SAFE(it, list, list_entry, next) { SLIST_REMOVE(list, it, http_header_t, list_entry); free(it); /* frees memory allocated for header, name, and value */ } } static esp_err_t http_add_content_length_header(http_context_t http_ctx, size_t value) { char size_str[11]; itoa(value, size_str, 10); return http_response_set_header(http_ctx, "Content-length", size_str); } static esp_err_t http_send_response_headers(http_context_t http_ctx) { assert(http_ctx->state == HTTP_COLLECTING_RESPONSE_HEADERS); /* Calculate total size of all the headers, allocate a buffer */ size_t total_headers_size = 0; /* response_code may be == 0, if we are sending headers for multipart * response part. In this case, don't send the response code line. */ if (http_ctx->response_code > 0) { total_headers_size += 16 /* HTTP/1.1, code, CRLF */ + strlen(http_response_code_to_str(http_ctx->response_code)); } http_header_t* it; SLIST_FOREACH(it, &http_ctx->response_headers, list_entry) { total_headers_size += strlen(it->name) + strlen(it->value) + 4 /* ": ", CRLF */; } total_headers_size += 3; /* Final CRLF, '\0' terminator */ char* headers_buf = calloc(1, total_headers_size); if (headers_buf == NULL) { return ESP_ERR_NO_MEM; } /* Write response */ size_t buf_size = total_headers_size; char* buf_ptr = headers_buf; int len; if (http_ctx->response_code > 0) { len = snprintf(buf_ptr, buf_size, "HTTP/1.1 %d %s\r\n", http_ctx->response_code, http_response_code_to_str(http_ctx->response_code)); assert(len < buf_size); buf_size -= len; buf_ptr += len; } /* Write response headers */ SLIST_FOREACH(it, &http_ctx->response_headers, list_entry) { len = snprintf(buf_ptr, buf_size, "%s: %s\r\n", it->name, it->value); assert(len < buf_size); buf_size -= len; buf_ptr += len; } /* Final CRLF */ len = snprintf(buf_ptr, buf_size, "\r\n"); assert(len < buf_size); buf_size -= len; buf_ptr += len; headers_list_clear(&http_ctx->response_headers); #if 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 */ static esp_err_t http_response_begin_common(http_context_t http_ctx, const char* content_type, size_t response_size) { esp_err_t err = http_response_set_header(http_ctx, "Content-type", content_type); if (err != ESP_OK) { return err; } http_ctx->expected_response_size = response_size; http_ctx->accumulated_response_size = 0; if (response_size != HTTP_RESPONSE_SIZE_UNKNOWN) { err = http_add_content_length_header(http_ctx, response_size); if (err != ESP_OK) { return err; } } return ESP_OK; } esp_err_t http_response_begin(http_context_t http_ctx, int code, const char* content_type, size_t response_size) { if (http_ctx->state != HTTP_COLLECTING_RESPONSE_HEADERS) { return ESP_ERR_INVALID_STATE; } http_ctx->response_code = code; return http_response_begin_common(http_ctx, content_type, response_size); } esp_err_t http_response_write(http_context_t http_ctx, const http_buffer_t* buffer) { size_t len; #if HTTPS_SERVER int ret; #endif esp_err_t err; if (http_ctx->state == HTTP_COLLECTING_RESPONSE_HEADERS) { err = http_send_response_headers(http_ctx); if (err != ESP_OK) { ESP_LOGE(TAG, "ERROR: in http_send_response_headers function..."); return err; } } len = buffer->size ? buffer->size : strlen((const char*) buffer->data); #if 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 } esp_err_t http_response_end(http_context_t http_ctx) { size_t expected = http_ctx->expected_response_size; size_t actual = http_ctx->accumulated_response_size; if (expected != HTTP_RESPONSE_SIZE_UNKNOWN && expected != actual) { ESP_LOGW(TAG, "Expected response size: %d, actual: %d", expected, actual); } http_ctx->state = HTTP_DONE; return ESP_OK; } esp_err_t http_response_begin_multipart(http_context_t http_ctx, const char* content_type, size_t response_size) { if (http_ctx->state == HTTP_COLLECTING_RESPONSE_HEADERS) { http_send_response_headers(http_ctx); http_ctx->response_code = 0; } http_ctx->state = HTTP_COLLECTING_RESPONSE_HEADERS; return http_response_begin_common(http_ctx, content_type, response_size); } esp_err_t http_response_end_multipart(http_context_t http_ctx, const char* boundary) { size_t expected = http_ctx->expected_response_size; size_t actual = http_ctx->accumulated_response_size; if (expected != HTTP_RESPONSE_SIZE_UNKNOWN && expected != actual) { ESP_LOGW(TAG, "Expected response size: %d, actual: %d", expected, actual); } /* reset expected_response_size so that http_response_end doesn't complain */ http_ctx->expected_response_size = HTTP_RESPONSE_SIZE_UNKNOWN; const http_buffer_t buf = { .data = boundary }; esp_err_t ret = http_response_write(http_ctx, &buf); http_ctx->state = HTTP_COLLECTING_RESPONSE_HEADERS; return ret; } static esp_err_t add_keyval_pair(http_header_list_t *list, const char* name, const char* val) { size_t name_len = strlen(name) + 1; size_t val_len = strlen(val) + 1; /* Allocate memory for the structure, name, and value, in one go */ size_t buf_len = sizeof(http_header_t) + name_len + val_len; char* buf = (char*) calloc(1, buf_len); if (buf == NULL) { return ESP_ERR_NO_MEM; } http_header_t* new_header = (http_header_t*) buf; new_header->name = buf + sizeof(http_header_t); new_header->value = new_header->name + name_len; strcpy(new_header->name, name); strcpy(new_header->value, val); SLIST_INSERT_HEAD(list, new_header, list_entry); return ESP_OK; } esp_err_t http_response_set_header(http_context_t http_ctx, const char* name, const char* val) { return add_keyval_pair(&http_ctx->response_headers, name, val); } static void http_send_not_found_response(http_context_t http_ctx) { 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); } static const char* http_response_code_to_str(int code) { switch (code) { case 200: return "OK"; case 204: return "No Content"; case 301: return "Moved Permanently"; case 302: return "Found"; case 400: return "Bad Request"; case 404: return "Not Found"; case 405: return "Method Not Allowed"; case 500: return "Internal Server Error"; default: return ""; } } static void http_handle_connection(http_server_t server, void *arg_conn) { unsigned char *buf; /* Single threaded server, one context only */ http_context_t ctx = &server->connection_context; /* Initialize context */ ctx->state = HTTP_PARSING_URI; #if 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; const http_parser_settings parser_settings = { .on_url = &http_url_cb, .on_headers_complete = &http_headers_done_cb, .on_header_field = &http_header_name_cb, .on_header_value = &http_header_value_cb, .on_body = &http_body_cb, .on_message_complete = &http_message_done_cb }; #if 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 HTTPS_SERVER if (ret > 0) { 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); } } #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; #if 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(ctx->conn); } if (inbuf) { netbuf_delete(inbuf); } #endif } static void http_server(void *arg) { uint8_t bits; http_server_t ctx = (http_server_t) arg; 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)) { #if 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_rootcacert_pem_start"); extern const unsigned char rootcacert_pem_end[] asm("_binary_rootcacert_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_servercert_pem_start"); extern const unsigned char servercert_pem_end[] asm("_binary_servercert_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_serverprvtkey_pem_start"); extern const unsigned char serverprvtkey_pem_end[] asm("_binary_serverprvtkey_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( ctx->cache ); #endif #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"); 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"); 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"); 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 ); #if defined(MBEDTLS_SSL_CACHE_C) mbedtls_ssl_conf_session_cache( ctx->conf, ctx->cache, mbedtls_ssl_cache_get, mbedtls_ssl_cache_set ); #endif 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; } mbedtls_ssl_conf_session_tickets_cb( &conf, mbedtls_ssl_ticket_write, mbedtls_ssl_ticket_parse, ctx->ticket_ctx ); #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_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 ); #if defined(MBEDTLS_SSL_CACHE_C) mbedtls_ssl_cache_free( ctx->cache ); #endif #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_bind(ctx->server_conn, NULL, ctx->port); if (err != ERR_OK) { goto out; } 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) { http_server_t ctx = calloc(1, sizeof(*ctx)); if (ctx == NULL) { return ESP_ERR_NO_MEM; } ctx->port = options->port; ctx->start_done = xEventGroupCreate(); if (ctx->start_done == NULL) { free(ctx); 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, "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..."); xEventGroupWaitBits(ctx->start_done, SERVER_STARTED_BIT, 0, 0, portMAX_DELAY); ESP_LOGI(TAG, "Server started!"); *out_server = ctx; return ESP_OK; } esp_err_t http_server_stop(http_server_t server) { /* FIXME: figure out a thread safe way to do this */ #if 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; } #if HTTPD_EXAMPLE 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; #if 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; 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"); 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; } 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; #if 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; ESP_ERROR_CHECK( res = http_server_start(&http_options, &server) ); if (res != ESP_OK) { return res; } ESP_ERROR_CHECK( res = http_register_form_handler(server, "/", HTTP_POST, HTTP_HANDLE_RESPONSE, &cb_POST_method, NULL) ); if (res != ESP_OK) { return res; } return res; } #endif // HTTPD_EXAMPLE