From: Ondřej Hruška Date: Wed, 31 Oct 2018 22:17:00 +0000 (+0100) Subject: Add a 'esp_https_server' component allowing to use http_server with OpenSSL X-Git-Tag: v3.3-beta1~92^2~4 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=4dd0fa61e900b6ccce11897a0a49d79b9d115636;p=esp-idf Add a 'esp_https_server' component allowing to use http_server with OpenSSL --- diff --git a/components/esp_https_server/CMakeLists.txt b/components/esp_https_server/CMakeLists.txt new file mode 100644 index 0000000000..dfdaa11ecc --- /dev/null +++ b/components/esp_https_server/CMakeLists.txt @@ -0,0 +1,7 @@ +set(COMPONENT_ADD_INCLUDEDIRS include) +set(COMPONENT_SRCS "src/https_server.c") + +set(COMPONENT_REQUIRES esp_http_server openssl) +set(COMPONENT_PRIV_REQUIRES lwip) + +register_component() diff --git a/components/esp_https_server/README.md b/components/esp_https_server/README.md new file mode 100644 index 0000000000..2d7bc7f8bc --- /dev/null +++ b/components/esp_https_server/README.md @@ -0,0 +1,44 @@ +# HTTPS server + +This component is built on top of `esp_http_server`. The HTTPS server takes advantage of hooks and +function overrides in the regular HTTP server to provide encryption using OpenSSL. + +All documentation for `esp_http_server` applies also to a server you create this way. + +## Used APIs + +The following API of `esp_http_server` should not be used with `esp_https_server`, as they are +used internally to handle secure sessions and to maintain internal state: + +- "send", "receive" and "pending" function overrides - secure socket handling + - `httpd_set_sess_send_override()` + - `httpd_set_sess_recv_override()` + - `httpd_set_sess_pending_override()` + - `httpd_set_send_override()` + - `httpd_set_recv_override()` + - `httpd_set_pending_override()` +- "transport context" - both global and session + - `httpd_sess_get_transport_ctx()` - returns SSL used for the session + - `httpd_sess_set_transport_ctx()` + - `httpd_get_global_transport_ctx()` - returns the shared SSL context + - `httpd_config_t.global_transport_ctx` + - `httpd_config_t.global_transport_ctx_free_fn` + - `httpd_config_t.open_fn` - used to set up secure sockets + +Everything else can be used without limitations. + +## Usage + +Please see the example `protocols/https_server` to learn how to set up a secure server. + +Basically all you need is to generate a certificate, embed it in the firmware, and provide +its pointers and lengths to the start function via the init struct. + +The server can be started with or without SSL by changing a flag in the init struct. +This could be used e.g. for testing or in trusted environments where you prefer speed over security. + +## Performance + +The initial session setup can take about two seconds, or more with slower clock speeds or more +verbose logging. Subsequent requests through the open secure socket are much faster (down to under +100 ms). diff --git a/components/esp_https_server/component.mk b/components/esp_https_server/component.mk new file mode 100644 index 0000000000..be1bffc586 --- /dev/null +++ b/components/esp_https_server/component.mk @@ -0,0 +1,2 @@ +COMPONENT_SRCDIRS := src +COMPONENT_ADD_INCLUDEDIRS := include diff --git a/components/esp_https_server/include/esp_https_server.h b/components/esp_https_server/include/esp_https_server.h new file mode 100644 index 0000000000..1bc9885874 --- /dev/null +++ b/components/esp_https_server/include/esp_https_server.h @@ -0,0 +1,112 @@ +// Copyright 2018 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef _ESP_HTTPS_SERVER_H_ +#define _ESP_HTTPS_SERVER_H_ + +#include +#include "esp_err.h" +#include "esp_http_server.h" + +/** + * HTTPS server config struct + * + * Please use HTTPD_SSL_CONFIG_DEFAULT() to initialize it. + */ +struct httpd_ssl_config { + /** + * Underlying HTTPD server config + * + * Parameters like task stack size and priority can be adjusted here. + */ + httpd_config_t httpd; + + /** CA certificate */ + const uint8_t *cacert_pem; + + /** CA certificate byte length */ + size_t cacert_len; + + /** Private key */ + const uint8_t *prvtkey_pem; + + /** Private key byte length */ + size_t prvtkey_len; + + /** Enable SSL (default true) */ + bool secure_enable; + + /** Port used when SSL is enabled (default 443) */ + uint16_t port_secure; + + /** Port used when SSL is disabled (default 80) */ + uint16_t port_insecure; +}; + +typedef struct httpd_ssl_config httpd_ssl_config_t; + +/** + * Default config struct init + * + * (http_server default config had to be copied for customization) + * + * Notes: + * - port is set when starting the server, according to 'secure_enable' + * - one socket uses ~ 40kB RAM with SSL, we reduce the default socket count to 4 + * - SSL sockets are usually long-lived, closing LRU prevents pool exhaustion DOS + * - Stack size may need adjustments depending on the user application + */ +#define HTTPD_SSL_CONFIG_DEFAULT() { \ + .httpd = { \ + .task_priority = tskIDLE_PRIORITY+5, \ + .stack_size = 10240, \ + .server_port = 0, \ + .ctrl_port = 32768, \ + .max_open_sockets = 4, \ + .max_uri_handlers = 8, \ + .max_resp_headers = 8, \ + .backlog_conn = 5, \ + .lru_purge_enable = true, \ + .recv_wait_timeout = 5, \ + .send_wait_timeout = 5, \ + .global_user_ctx = NULL, \ + .global_user_ctx_free_fn = NULL, \ + .global_transport_ctx = NULL, \ + .global_transport_ctx_free_fn = NULL, \ + .open_fn = NULL, \ + .close_fn = NULL, \ + }, \ + .secure_enable = true, \ + .port_secure = 443, \ + .port_insecure = 80, \ +} + +/** + * Create a SSL capable HTTP server (secure mode may be disabled in config) + * + * @param[in,out] config - server config, must not be const. Does not have to stay valid after + * calling this function. + * @param[out] handle - storage for the server handle, must be a valid pointer + * @return success + */ +esp_err_t httpd_ssl_start(httpd_handle_t *handle, httpd_ssl_config_t *config); + +/** + * Stop the server. Blocks until the server is shut down. + * + * @param[in] handle + */ +void httpd_ssl_stop(httpd_handle_t handle); + +#endif // _ESP_HTTPS_SERVER_H_ diff --git a/components/esp_https_server/src/https_server.c b/components/esp_https_server/src/https_server.c new file mode 100644 index 0000000000..0cf741b374 --- /dev/null +++ b/components/esp_https_server/src/https_server.c @@ -0,0 +1,220 @@ +// Copyright 2018 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include "esp_https_server.h" +#include "openssl/ssl.h" +#include "esp_log.h" +#include "sdkconfig.h" + +const static char *TAG = "esp_https_server"; + +/** + * SSL socket close handler + * + * @param[in] ctx - session transport context (SSL context we stored there) + */ +static void httpd_ssl_close(void *ctx) +{ + assert(ctx != NULL); + SSL_shutdown(ctx); + SSL_free(ctx); + ESP_LOGD(TAG, "Secure socket closed"); +} + +/** + * SSL socket pending-check function + * + * @param server + * @param sockfd + * @return number of pending bytes, negative on error + */ +static int httpd_ssl_pending(httpd_handle_t server, int sockfd) +{ + SSL *ssl = httpd_sess_get_transport_ctx(server, sockfd); + assert(ssl != NULL); + return SSL_pending(ssl); +} + +/** + * Receive from a SSL socket + * + * @param server + * @param sockfd + * @param buf + * @param buf_len + * @param flags + * @return bytes read, negative on error + */ +static int httpd_ssl_recv(httpd_handle_t server, int sockfd, char *buf, size_t buf_len, int flags) +{ + SSL *ssl = httpd_sess_get_transport_ctx(server, sockfd); + assert(ssl != NULL); + return SSL_read(ssl, buf, buf_len); +} + +/** + * Send to a SSL socket + * + * @param server + * @param sockfd + * @param buf + * @param buf_len + * @param flags + * @return bytes sent, negative on error + */ +static int httpd_ssl_send(httpd_handle_t server, int sockfd, const char *buf, size_t buf_len, int flags) +{ + SSL *ssl = httpd_sess_get_transport_ctx(server, sockfd); + assert(ssl != NULL); + return SSL_write(ssl, buf, buf_len); +} + +/** + * Open a SSL socket for the server. + * The fd is already open and ready to read / write raw data. + * + * @param server + * @param sockfd - raw socket fd + * @return success + */ +static esp_err_t httpd_ssl_open(httpd_handle_t server, int sockfd) +{ + assert(server != NULL); + + // Retrieve the SSL context from the global context field (set in config) + SSL_CTX *global_ctx = httpd_get_global_transport_ctx(server); + assert(global_ctx != NULL); + + SSL *ssl = SSL_new(global_ctx); + if (NULL == ssl) { + ESP_LOGE(TAG, "SSL_new ret NULL (out of memory)"); + return ESP_ERR_NO_MEM; + } + + if (1 != SSL_set_fd(ssl, sockfd)) { + ESP_LOGE(TAG, "fail to set SSL fd"); + goto teardown; + } + + ESP_LOGD(TAG, "SSL accept"); + if (1 != SSL_accept(ssl)) { + ESP_LOGW(TAG, "fail to SSL_accept - handshake error"); + goto teardown; + } + + // Store the SSL session into the context field of the HTTPD session object + httpd_sess_set_transport_ctx(server, sockfd, ssl, httpd_ssl_close); + + // Set rx/tx/pending override functions + httpd_set_sess_send_override(server, sockfd, httpd_ssl_send); + httpd_set_sess_recv_override(server, sockfd, httpd_ssl_recv); + httpd_set_sess_pending_override(server, sockfd, httpd_ssl_pending); + + // all access should now go through SSL + + ESP_LOGD(TAG, "Secure socket open"); + + return ESP_OK; + +teardown: + SSL_free(ssl); + return ESP_FAIL; +} + +/** + * Tear down the HTTPD global transport context + * + * @param ctx + */ +static void free_secure_context(void *ctx) +{ + assert(ctx != NULL); + + ESP_LOGI(TAG, "Server shuts down, releasing SSL context"); + SSL_CTX_free(ctx); +} + +/** +* Create and perform basic init of a SSL_CTX, or return NULL on failure +* +* @return ctx or null +*/ +static SSL_CTX *create_secure_context(const struct httpd_ssl_config *config) +{ + SSL_CTX *ctx = NULL; + + ESP_LOGD(TAG, "SSL server context create"); + ctx = SSL_CTX_new(TLS_server_method()); + if (NULL != ctx) { + //region SSL ctx alloc'd + ESP_LOGD(TAG, "SSL ctx set own cert"); + if (SSL_CTX_use_certificate_ASN1(ctx, config->cacert_len, config->cacert_pem) + && SSL_CTX_use_PrivateKey_ASN1(0, ctx, config->prvtkey_pem, (long) config->prvtkey_len)) { + return ctx; + } + else { + ESP_LOGE(TAG, "Failed to set certificate"); + SSL_CTX_free(ctx); + ctx = NULL; + } + } else { + ESP_LOGE(TAG, "Failed to create SSL context"); + } + return NULL; +} + +/** Start the server */ +esp_err_t httpd_ssl_start(httpd_handle_t *pHandle, struct httpd_ssl_config *config) +{ + assert(config != NULL); + assert(pHandle != NULL); + + ESP_LOGI(TAG, "Starting server"); + + if (config->secure_enable) { + SSL_CTX *ctx = create_secure_context(config); + if (!ctx) { + return ESP_FAIL; + } + + ESP_LOGD(TAG, "SSL context ready"); + + // set SSL specific config + config->httpd.global_transport_ctx = ctx; + config->httpd.global_transport_ctx_free_fn = free_secure_context; + config->httpd.open_fn = httpd_ssl_open; // the open function configures the created SSL sessions + + config->httpd.server_port = config->port_secure; + } else { + ESP_LOGD(TAG, "SSL disabled, using plain HTTP"); + config->httpd.server_port = config->port_insecure; + } + + httpd_handle_t handle = NULL; + + esp_err_t ret = httpd_start(&handle, &config->httpd); + if (ret != ESP_OK) return ret; + + *pHandle = handle; + + ESP_LOGI(TAG, "Server listening on port %d", config->httpd.server_port); + return ESP_OK; +} + +/** Stop the server */ +void httpd_ssl_stop(httpd_handle_t handle) +{ + httpd_stop(handle); +} diff --git a/examples/protocols/https_server/CMakeLists.txt b/examples/protocols/https_server/CMakeLists.txt new file mode 100644 index 0000000000..7f58e025f3 --- /dev/null +++ b/examples/protocols/https_server/CMakeLists.txt @@ -0,0 +1,6 @@ +# The following lines of boilerplate have to be in your project's CMakeLists +# in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.5) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(https_server) diff --git a/examples/protocols/https_server/Makefile b/examples/protocols/https_server/Makefile new file mode 100644 index 0000000000..5a20b6f57e --- /dev/null +++ b/examples/protocols/https_server/Makefile @@ -0,0 +1,9 @@ +# +# This is a project Makefile. It is assumed the directory this Makefile resides in is a +# project subdirectory. +# + +PROJECT_NAME := https_server + +include $(IDF_PATH)/make/project.mk + diff --git a/examples/protocols/https_server/README.md b/examples/protocols/https_server/README.md new file mode 100644 index 0000000000..c89c0f79fa --- /dev/null +++ b/examples/protocols/https_server/README.md @@ -0,0 +1,15 @@ +# HTTP server with SSL support using OpenSSL + +This example creates a SSL server that returns a simple HTML page when you visit its root URL. + +See the `esp_https_server` component documentation for details. + +## Certificates + +You will need to approve a security exception in your browser. This is because of a self signed +certificate; this will be always the case, unless you preload the CA root into your browser/system +as trusted. + +You can generate a new certificate using the OpenSSL command line tool as shown in the script +`main/certs/gencert.sh`. It is **strongly recommended** to not reuse the example certificate in +your application; it is included only for demonstration. diff --git a/examples/protocols/https_server/main/CMakeLists.txt b/examples/protocols/https_server/main/CMakeLists.txt new file mode 100644 index 0000000000..9e08fb4d71 --- /dev/null +++ b/examples/protocols/https_server/main/CMakeLists.txt @@ -0,0 +1,8 @@ +set(COMPONENT_SRCS "main.c") +set(COMPONENT_ADD_INCLUDEDIRS ".") + +set(COMPONENT_EMBED_TXTFILES + "certs/cacert.pem" + "certs/prvtkey.pem") + +register_component() diff --git a/examples/protocols/https_server/main/Kconfig.projbuild b/examples/protocols/https_server/main/Kconfig.projbuild new file mode 100644 index 0000000000..9e2813c697 --- /dev/null +++ b/examples/protocols/https_server/main/Kconfig.projbuild @@ -0,0 +1,16 @@ +menu "Example Configuration" + +config WIFI_SSID + string "WiFi SSID" + default "myssid" + help + SSID (network name) for the example to connect to. + +config WIFI_PASSWORD + string "WiFi Password" + default "mypassword" + help + WiFi password (WPA or WPA2) for the example to use. + Can be left blank if the network has no security set. + +endmenu diff --git a/examples/protocols/https_server/main/certs/cacert.pem b/examples/protocols/https_server/main/certs/cacert.pem new file mode 100644 index 0000000000..cd2b80c824 --- /dev/null +++ b/examples/protocols/https_server/main/certs/cacert.pem @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDKzCCAhOgAwIBAgIUBxM3WJf2bP12kAfqhmhhjZWv0ukwDQYJKoZIhvcNAQEL +BQAwJTEjMCEGA1UEAwwaRVNQMzIgSFRUUFMgc2VydmVyIGV4YW1wbGUwHhcNMTgx +MDE3MTEzMjU3WhcNMjgxMDE0MTEzMjU3WjAlMSMwIQYDVQQDDBpFU1AzMiBIVFRQ +UyBzZXJ2ZXIgZXhhbXBsZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB +ALBint6nP77RCQcmKgwPtTsGK0uClxg+LwKJ3WXuye3oqnnjqJCwMEneXzGdG09T +sA0SyNPwrEgebLCH80an3gWU4pHDdqGHfJQa2jBL290e/5L5MB+6PTs2NKcojK/k +qcZkn58MWXhDW1NpAnJtjVniK2Ksvr/YIYSbyD+JiEs0MGxEx+kOl9d7hRHJaIzd +GF/vO2pl295v1qXekAlkgNMtYIVAjUy9CMpqaQBCQRL+BmPSJRkXBsYk8GPnieS4 +sUsp53DsNvCCtWDT6fd9D1v+BB6nDk/FCPKhtjYOwOAZlX4wWNSZpRNr5dfrxKsb +jAn4PCuR2akdF4G8WLUeDWECAwEAAaNTMFEwHQYDVR0OBBYEFMnmdJKOEepXrHI/ +ivM6mVqJgAX8MB8GA1UdIwQYMBaAFMnmdJKOEepXrHI/ivM6mVqJgAX8MA8GA1Ud +EwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBADiXIGEkSsN0SLSfCF1VNWO3 +emBurfOcDq4EGEaxRKAU0814VEmU87btIDx80+z5Dbf+GGHCPrY7odIkxGNn0DJY +W1WcF+DOcbiWoUN6DTkAML0SMnp8aGj9ffx3x+qoggT+vGdWVVA4pgwqZT7Ybntx +bkzcNFW0sqmCv4IN1t4w6L0A87ZwsNwVpre/j6uyBw7s8YoJHDLRFT6g7qgn0tcN +ZufhNISvgWCVJQy/SZjNBHSpnIdCUSJAeTY2mkM4sGxY0Widk8LnjydxZUSxC3Nl +hb6pnMh3jRq4h0+5CZielA4/a+TdrNPv/qok67ot/XJdY3qHCCd8O2b14OVq9jo= +-----END CERTIFICATE----- diff --git a/examples/protocols/https_server/main/certs/gencert.sh b/examples/protocols/https_server/main/certs/gencert.sh new file mode 100644 index 0000000000..5b2f79dc45 --- /dev/null +++ b/examples/protocols/https_server/main/certs/gencert.sh @@ -0,0 +1,8 @@ +#!/bin/bash + +# Example command to generate a new certificate in the correct format. +# Expiry time and metadata fields can be adjusted in the invocation. + +# Please see the openssl man pages (man openssl-req) for more details + +openssl req -newkey rsa:2048 -nodes -keyout prvtkey.pem -x509 -days 3650 -out cacert.pem -subj "/CN=ESP32 HTTPS server example" diff --git a/examples/protocols/https_server/main/certs/prvtkey.pem b/examples/protocols/https_server/main/certs/prvtkey.pem new file mode 100644 index 0000000000..70d29078c4 --- /dev/null +++ b/examples/protocols/https_server/main/certs/prvtkey.pem @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCwYp7epz++0QkH +JioMD7U7BitLgpcYPi8Cid1l7snt6Kp546iQsDBJ3l8xnRtPU7ANEsjT8KxIHmyw +h/NGp94FlOKRw3ahh3yUGtowS9vdHv+S+TAfuj07NjSnKIyv5KnGZJ+fDFl4Q1tT +aQJybY1Z4itirL6/2CGEm8g/iYhLNDBsRMfpDpfXe4URyWiM3Rhf7ztqZdveb9al +3pAJZIDTLWCFQI1MvQjKamkAQkES/gZj0iUZFwbGJPBj54nkuLFLKedw7DbwgrVg +0+n3fQ9b/gQepw5PxQjyobY2DsDgGZV+MFjUmaUTa+XX68SrG4wJ+DwrkdmpHReB +vFi1Hg1hAgMBAAECggEAaTCnZkl/7qBjLexIryC/CBBJyaJ70W1kQ7NMYfniWwui +f0aRxJgOdD81rjTvkINsPp+xPRQO6oOadjzdjImYEuQTqrJTEUnntbu924eh+2D9 +Mf2CAanj0mglRnscS9mmljZ0KzoGMX6Z/EhnuS40WiJTlWlH6MlQU/FDnwC6U34y +JKy6/jGryfsx+kGU/NRvKSru6JYJWt5v7sOrymHWD62IT59h3blOiP8GMtYKeQlX +49om9Mo1VTIFASY3lrxmexbY+6FG8YO+tfIe0tTAiGrkb9Pz6tYbaj9FjEWOv4Vc ++3VMBUVdGJjgqvE8fx+/+mHo4Rg69BUPfPSrpEg7sQKBgQDlL85G04VZgrNZgOx6 +pTlCCl/NkfNb1OYa0BELqWINoWaWQHnm6lX8YjrUjwRpBF5s7mFhguFjUjp/NW6D +0EEg5BmO0ePJ3dLKSeOA7gMo7y7kAcD/YGToqAaGljkBI+IAWK5Su5yldrECTQKG +YnMKyQ1MWUfCYEwHtPvFvE5aPwKBgQDFBWXekpxHIvt/B41Cl/TftAzE7/f58JjV +MFo/JCh9TDcH6N5TMTRS1/iQrv5M6kJSSrHnq8pqDXOwfHLwxetpk9tr937VRzoL +CuG1Ar7c1AO6ujNnAEmUVC2DppL/ck5mRPWK/kgLwZSaNcZf8sydRgphsW1ogJin +7g0nGbFwXwKBgQCPoZY07Pr1TeP4g8OwWTu5F6dSvdU2CAbtZthH5q98u1n/cAj1 +noak1Srpa3foGMTUn9CHu+5kwHPIpUPNeAZZBpq91uxa5pnkDMp3UrLIRJ2uZyr8 +4PxcknEEh8DR5hsM/IbDcrCJQglM19ZtQeW3LKkY4BsIxjDf45ymH407IQKBgE/g +Ul6cPfOxQRlNLH4VMVgInSyyxWx1mODFy7DRrgCuh5kTVh+QUVBM8x9lcwAn8V9/ +nQT55wR8E603pznqY/jX0xvAqZE6YVPcw4kpZcwNwL1RhEl8GliikBlRzUL3SsW3 +q30AfqEViHPE3XpE66PPo6Hb1ymJCVr77iUuC3wtAoGBAIBrOGunv1qZMfqmwAY2 +lxlzRgxgSiaev0lTNxDzZkmU/u3dgdTwJ5DDANqPwJc6b8SGYTp9rQ0mbgVHnhIB +jcJQBQkTfq6Z0H6OoTVi7dPs3ibQJFrtkoyvYAbyk36quBmNRjVh6rc8468bhXYr +v/t+MeGJP/0Zw8v/X2CFll96 +-----END PRIVATE KEY----- diff --git a/examples/protocols/https_server/main/component.mk b/examples/protocols/https_server/main/component.mk new file mode 100644 index 0000000000..b120d66e5e --- /dev/null +++ b/examples/protocols/https_server/main/component.mk @@ -0,0 +1,7 @@ +# +# "main" pseudo-component makefile. +# +# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.) + +COMPONENT_EMBED_TXTFILES := certs/cacert.pem +COMPONENT_EMBED_TXTFILES += certs/prvtkey.pem diff --git a/examples/protocols/https_server/main/main.c b/examples/protocols/https_server/main/main.c new file mode 100644 index 0000000000..d46897f1bd --- /dev/null +++ b/examples/protocols/https_server/main/main.c @@ -0,0 +1,150 @@ +/* Simple HTTP + SSL Server Example + + This example code is in the Public Domain (or CC0 licensed, at your option.) + + Unless required by applicable law or agreed to in writing, this + software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. +*/ + +#include +#include +#include +#include +#include +#include + +#include + +/* A simple example that demonstrates how to create GET and POST + * handlers for the web server. + * The examples use simple WiFi configuration that you can set via + * 'make menuconfig'. + * If you'd rather not, just change the below entries to strings + * with the config you want - + * ie. #define EXAMPLE_WIFI_SSID "mywifissid" +*/ +#define EXAMPLE_WIFI_SSID CONFIG_WIFI_SSID +#define EXAMPLE_WIFI_PASS CONFIG_WIFI_PASSWORD + +static const char *TAG="APP"; + + +/* An HTTP GET handler */ +esp_err_t root_get_handler(httpd_req_t *req) +{ + httpd_resp_set_type(req, "text/html"); + httpd_resp_send(req, "

Hello Secure World!

", -1); // -1 = use strlen() + + return ESP_OK; +} + +const httpd_uri_t root = { + .uri = "/", + .method = HTTP_GET, + .handler = root_get_handler +}; + + +httpd_handle_t start_webserver(void) +{ + httpd_handle_t server = NULL; + + // Start the httpd server + ESP_LOGI(TAG, "Starting server"); + + httpd_ssl_config_t conf = HTTPD_SSL_CONFIG_DEFAULT(); + + extern const unsigned char cacert_pem_start[] asm("_binary_cacert_pem_start"); + extern const unsigned char cacert_pem_end[] asm("_binary_cacert_pem_end"); + conf.cacert_pem = cacert_pem_start; + conf.cacert_len = cacert_pem_end - cacert_pem_start; + + extern const unsigned char prvtkey_pem_start[] asm("_binary_prvtkey_pem_start"); + extern const unsigned char prvtkey_pem_end[] asm("_binary_prvtkey_pem_end"); + conf.prvtkey_pem = prvtkey_pem_start; + conf.prvtkey_len = prvtkey_pem_end - prvtkey_pem_start; + + esp_err_t ret = httpd_ssl_start(&server, &conf); + if (ESP_OK != ret) { + ESP_LOGI(TAG, "Error starting server!"); + return NULL; + } + + // Set URI handlers + ESP_LOGI(TAG, "Registering URI handlers"); + httpd_register_uri_handler(server, &root); + return server; +} + +void stop_webserver(httpd_handle_t server) +{ + // Stop the httpd server + httpd_ssl_stop(server); +} + + + + +// ------------------------- application boilerplate ------------------------ + +static esp_err_t event_handler(void *ctx, system_event_t *event) +{ + httpd_handle_t *server = (httpd_handle_t *) ctx; + + switch(event->event_id) { + case SYSTEM_EVENT_STA_START: + ESP_LOGI(TAG, "SYSTEM_EVENT_STA_START"); + ESP_ERROR_CHECK(esp_wifi_connect()); + break; + case SYSTEM_EVENT_STA_GOT_IP: + ESP_LOGI(TAG, "SYSTEM_EVENT_STA_GOT_IP"); + ESP_LOGI(TAG, "Got IP: '%s'", + ip4addr_ntoa(&event->event_info.got_ip.ip_info.ip)); + + /* Start the web server */ + if (*server == NULL) { + *server = start_webserver(); + } + break; + case SYSTEM_EVENT_STA_DISCONNECTED: + ESP_LOGI(TAG, "SYSTEM_EVENT_STA_DISCONNECTED"); + ESP_ERROR_CHECK(esp_wifi_connect()); + + /* Stop the web server */ + if (*server) { + stop_webserver(*server); + *server = NULL; + } + break; + default: + break; + } + return ESP_OK; +} + +static void initialise_wifi(void *arg) +{ + tcpip_adapter_init(); + ESP_ERROR_CHECK(esp_event_loop_init(event_handler, arg)); + wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); + ESP_ERROR_CHECK(esp_wifi_init(&cfg)); + ESP_ERROR_CHECK(esp_wifi_set_storage(WIFI_STORAGE_RAM)); + wifi_config_t wifi_config = { + .sta = { + .ssid = EXAMPLE_WIFI_SSID, + .password = EXAMPLE_WIFI_PASS, + }, + }; + ESP_LOGI(TAG, "Setting WiFi configuration SSID %s...", wifi_config.sta.ssid); + ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA)); + ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config)); + ESP_ERROR_CHECK(esp_wifi_start()); +} + +void app_main() +{ + static httpd_handle_t server = NULL; + ESP_ERROR_CHECK(nvs_flash_init()); + initialise_wifi(&server); +}