From d2d0b981c72293336165782cf843353988ca9ca7 Mon Sep 17 00:00:00 2001
From: Khoi Hoang <57012152+khoih-prog@users.noreply.github.com>
Date: Mon, 26 Sep 2022 20:40:42 -0400
Subject: [PATCH 1/5] Fix compile error
Use from ESP32 latest core v2.0.0+
```
#include
```
---
src/HTTPConnection.hpp | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/src/HTTPConnection.hpp b/src/HTTPConnection.hpp
index fb15d7a..60e78b4 100644
--- a/src/HTTPConnection.hpp
+++ b/src/HTTPConnection.hpp
@@ -6,7 +6,12 @@
#include
#include
-#include
+
+//KH mod
+//#include
+#include
+//////
+
#include
// Required for sockets
From 3226dc8b0b60438d6da3912d5a06847620afc515 Mon Sep 17 00:00:00 2001
From: Khoi Hoang <57012152+khoih-prog@users.noreply.github.com>
Date: Mon, 26 Sep 2022 22:09:20 -0400
Subject: [PATCH 2/5] Add examples for WT32_ETH01
---
.../WT32_ETH01/Async-Server/Async-Server.ino | 195 ++++++
examples/WT32_ETH01/Async-Server/cert.h | 4 +
.../WT32_ETH01/Async-Server/private_key.h | 4 +
.../Authentication/Authentication.ino | 403 +++++++++++
examples/WT32_ETH01/Authentication/cert.h | 4 +
.../WT32_ETH01/Authentication/private_key.h | 4 +
examples/WT32_ETH01/HTML-Forms/HTML-Forms.ino | 487 +++++++++++++
examples/WT32_ETH01/HTML-Forms/cert.h | 4 +
examples/WT32_ETH01/HTML-Forms/private_key.h | 4 +
.../HTTPS-and-HTTP/HTTPS-and-HTTP.ino | 176 +++++
examples/WT32_ETH01/HTTPS-and-HTTP/cert.h | 4 +
.../WT32_ETH01/HTTPS-and-HTTP/private_key.h | 4 +
examples/WT32_ETH01/Middleware/Middleware.ino | 188 +++++
examples/WT32_ETH01/Middleware/cert.h | 4 +
examples/WT32_ETH01/Middleware/private_key.h | 4 +
.../Parameter-Validation.ino | 312 +++++++++
.../WT32_ETH01/Parameter-Validation/cert.h | 4 +
.../Parameter-Validation/private_key.h | 4 +
examples/WT32_ETH01/Parameters/Parameters.ino | 409 +++++++++++
examples/WT32_ETH01/Parameters/cert.h | 4 +
examples/WT32_ETH01/Parameters/private_key.h | 4 +
.../Put-Post-Echo/Put-Post-Echo.ino | 212 ++++++
examples/WT32_ETH01/Put-Post-Echo/cert.h | 4 +
.../WT32_ETH01/Put-Post-Echo/private_key.h | 4 +
examples/WT32_ETH01/REST-API/REST-API.ino | 641 ++++++++++++++++++
examples/WT32_ETH01/REST-API/cert.h | 4 +
.../REST-API/data/public/index.html | 230 +++++++
.../REST-API/data/public/jquery-3.3.1.min.js | 2 +
examples/WT32_ETH01/REST-API/private_key.h | 4 +
.../Self-Signed-Certificate.ino | 206 ++++++
.../WT32_ETH01/Self-Signed-Certificate/cert.h | 4 +
.../Self-Signed-Certificate/private_key.h | 4 +
.../WT32_ETH01/Static-Page/Static-Page.ino | 181 +++++
examples/WT32_ETH01/Static-Page/cert.h | 4 +
examples/WT32_ETH01/Static-Page/favicon.h | 10 +
examples/WT32_ETH01/Static-Page/private_key.h | 4 +
.../Websocket-Chat/Websocket-Chat.ino | 336 +++++++++
examples/WT32_ETH01/Websocket-Chat/cert.h | 4 +
.../WT32_ETH01/Websocket-Chat/private_key.h | 4 +
39 files changed, 4084 insertions(+)
create mode 100644 examples/WT32_ETH01/Async-Server/Async-Server.ino
create mode 100644 examples/WT32_ETH01/Async-Server/cert.h
create mode 100644 examples/WT32_ETH01/Async-Server/private_key.h
create mode 100644 examples/WT32_ETH01/Authentication/Authentication.ino
create mode 100644 examples/WT32_ETH01/Authentication/cert.h
create mode 100644 examples/WT32_ETH01/Authentication/private_key.h
create mode 100644 examples/WT32_ETH01/HTML-Forms/HTML-Forms.ino
create mode 100644 examples/WT32_ETH01/HTML-Forms/cert.h
create mode 100644 examples/WT32_ETH01/HTML-Forms/private_key.h
create mode 100644 examples/WT32_ETH01/HTTPS-and-HTTP/HTTPS-and-HTTP.ino
create mode 100644 examples/WT32_ETH01/HTTPS-and-HTTP/cert.h
create mode 100644 examples/WT32_ETH01/HTTPS-and-HTTP/private_key.h
create mode 100644 examples/WT32_ETH01/Middleware/Middleware.ino
create mode 100644 examples/WT32_ETH01/Middleware/cert.h
create mode 100644 examples/WT32_ETH01/Middleware/private_key.h
create mode 100644 examples/WT32_ETH01/Parameter-Validation/Parameter-Validation.ino
create mode 100644 examples/WT32_ETH01/Parameter-Validation/cert.h
create mode 100644 examples/WT32_ETH01/Parameter-Validation/private_key.h
create mode 100644 examples/WT32_ETH01/Parameters/Parameters.ino
create mode 100644 examples/WT32_ETH01/Parameters/cert.h
create mode 100644 examples/WT32_ETH01/Parameters/private_key.h
create mode 100644 examples/WT32_ETH01/Put-Post-Echo/Put-Post-Echo.ino
create mode 100644 examples/WT32_ETH01/Put-Post-Echo/cert.h
create mode 100644 examples/WT32_ETH01/Put-Post-Echo/private_key.h
create mode 100644 examples/WT32_ETH01/REST-API/REST-API.ino
create mode 100644 examples/WT32_ETH01/REST-API/cert.h
create mode 100644 examples/WT32_ETH01/REST-API/data/public/index.html
create mode 100644 examples/WT32_ETH01/REST-API/data/public/jquery-3.3.1.min.js
create mode 100644 examples/WT32_ETH01/REST-API/private_key.h
create mode 100644 examples/WT32_ETH01/Self-Signed-Certificate/Self-Signed-Certificate.ino
create mode 100644 examples/WT32_ETH01/Self-Signed-Certificate/cert.h
create mode 100644 examples/WT32_ETH01/Self-Signed-Certificate/private_key.h
create mode 100644 examples/WT32_ETH01/Static-Page/Static-Page.ino
create mode 100644 examples/WT32_ETH01/Static-Page/cert.h
create mode 100644 examples/WT32_ETH01/Static-Page/favicon.h
create mode 100644 examples/WT32_ETH01/Static-Page/private_key.h
create mode 100644 examples/WT32_ETH01/Websocket-Chat/Websocket-Chat.ino
create mode 100644 examples/WT32_ETH01/Websocket-Chat/cert.h
create mode 100644 examples/WT32_ETH01/Websocket-Chat/private_key.h
diff --git a/examples/WT32_ETH01/Async-Server/Async-Server.ino b/examples/WT32_ETH01/Async-Server/Async-Server.ino
new file mode 100644
index 0000000..f8f2930
--- /dev/null
+++ b/examples/WT32_ETH01/Async-Server/Async-Server.ino
@@ -0,0 +1,195 @@
+/**
+ Example for the WT32_ETH01 HTTP(S) Webserver
+
+ IMPORTANT NOTE:
+ To run this script, your need to
+ 1) Make sure to have certificate data available. You will find a
+ shell script (create_cert.sh) and instructions to do so in the library folder
+ under extras/
+
+ This script will install an HTTPS Server on your WT32_ETH01 with the following
+ functionalities:
+ - Show simple page on web server root
+ - 404 for everything else
+ The server will be run in a separate task, so that you can do your own stuff
+ in the loop() function.
+ Everything else is just like the Static-Page example
+*/
+
+/** Check if we have multiple cores */
+#if CONFIG_FREERTOS_UNICORE
+ #define ARDUINO_RUNNING_CORE 0
+#else
+ #define ARDUINO_RUNNING_CORE 1
+#endif
+
+// Include certificate data (see note above)
+#include "cert.h"
+#include "private_key.h"
+
+//////////////////////////////////////////////////
+
+// For WT32_ETH01
+#define DEBUG_ETHERNET_WEBSERVER_PORT Serial
+
+// Debug Level from 0 to 4
+#define _ETHERNET_WEBSERVER_LOGLEVEL_ 3
+
+#include
+
+// Select the IP address according to your local network
+IPAddress myIP(192, 168, 2, 232);
+IPAddress myGW(192, 168, 2, 1);
+IPAddress mySN(255, 255, 255, 0);
+
+// Google DNS Server IP
+IPAddress myDNS(8, 8, 8, 8);
+
+//////////////////////////////////////////////////
+
+// Includes for the server
+#include
+#include
+#include
+#include
+
+// The HTTPS Server comes in a separate namespace. For easier use, include it here.
+using namespace httpsserver;
+
+// Create an SSL certificate object from the files included above
+SSLCert cert = SSLCert(
+ example_crt_DER, example_crt_DER_len,
+ example_key_DER, example_key_DER_len
+ );
+
+// Create an SSL-enabled server that uses the certificate
+HTTPSServer secureServer = HTTPSServer(&cert);
+
+void serverTask(void *params)
+{
+ // In the separate task we first do everything that we would have done in the
+ // setup() function, if we would run the server synchronously.
+
+ // Note: The second task has its own stack, so you need to think about where
+ // you create the server's resources and how to make sure that the server
+ // can access everything it needs to access. Also make sure that concurrent
+ // access is no problem in your sketch or implement countermeasures like locks
+ // or mutexes.
+
+ // Create nodes
+ ResourceNode * nodeRoot = new ResourceNode("/", "GET", &handleRoot);
+ ResourceNode * node404 = new ResourceNode("", "GET", &handle404);
+
+ // Add nodes to the server
+ secureServer.registerNode(nodeRoot);
+ secureServer.setDefaultNode(node404);
+
+ Serial.println("Starting server...");
+ secureServer.start();
+
+ if (secureServer.isRunning())
+ {
+ Serial.println("Server ready.");
+
+ // "loop()" function of the separate task
+ while (true)
+ {
+ // This call will let the server do its work
+ secureServer.loop();
+
+ // Other code would go here...
+ delay(1);
+ }
+ }
+}
+
+void handleRoot(HTTPRequest * req, HTTPResponse * res)
+{
+ // Status code is 200 OK by default.
+ // We want to deliver a simple HTML page, so we send a corresponding content type:
+ res->setHeader("Content-Type", "text/html");
+
+ // The response implements the Print interface, so you can use it just like
+ // you would write to Serial etc.
+ res->println("");
+ res->println("");
+ res->println("Hello World!");
+ res->println("");
+ res->println("
Hello World!
");
+ res->print("
Your server is running for ");
+ // A bit of dynamic data: Show the uptime
+ res->print((int)(millis() / 1000), DEC);
+ res->println(" seconds.
");
+ res->println("");
+ res->println("");
+}
+
+void handle404(HTTPRequest * req, HTTPResponse * res)
+{
+ // Discard request body, if we received any
+ // We do this, as this is the default node and may also server POST/PUT requests
+ req->discardRequestBody();
+
+ // Set the response status
+ res->setStatusCode(404);
+ res->setStatusText("Not Found");
+
+ // Set content type of the response
+ res->setHeader("Content-Type", "text/html");
+
+ // Write a tiny HTTP page
+ res->println("");
+ res->println("");
+ res->println("Not Found");
+ res->println("
404 Not Found
The requested resource was not found on this server.
");
+ res->println("");
+}
+
+void setup()
+{
+ // For logging
+ Serial.begin(115200);
+ while (!Serial && millis() < 5000);
+
+ ///////////////////////////////////////////////
+
+ Serial.print("\nStarting Async_Server on " + String(ARDUINO_BOARD));
+ Serial.println(" with " + String(SHIELD_TYPE));
+ Serial.println(WEBSERVER_WT32_ETH01_VERSION);
+
+ // To be called before ETH.begin()
+ WT32_ETH01_onEvent();
+
+ //bool begin(uint8_t phy_addr=ETH_PHY_ADDR, int power=ETH_PHY_POWER, int mdc=ETH_PHY_MDC, int mdio=ETH_PHY_MDIO,
+ // eth_phy_type_t type=ETH_PHY_TYPE, eth_clock_mode_t clk_mode=ETH_CLK_MODE);
+ //ETH.begin(ETH_PHY_ADDR, ETH_PHY_POWER, ETH_PHY_MDC, ETH_PHY_MDIO, ETH_PHY_TYPE, ETH_CLK_MODE);
+ ETH.begin(ETH_PHY_ADDR, ETH_PHY_POWER);
+
+ // Static IP, leave without this line to get IP via DHCP
+ //bool config(IPAddress local_ip, IPAddress gateway, IPAddress subnet, IPAddress dns1 = 0, IPAddress dns2 = 0);
+ ETH.config(myIP, myGW, mySN, myDNS);
+
+ WT32_ETH01_waitForConnect();
+
+ Serial.print(F("HTTP EthernetWebServer is @ IP : "));
+ Serial.println(ETH.localIP());
+
+ ///////////////////////////////////////////////
+
+ // Setup the server as a separate task.
+ Serial.println("Creating server task... ");
+ // We pass:
+ // serverTask - the function that should be run as separate task
+ // "https443" - a name for the task (mainly used for logging)
+ // 6144 - stack size in byte. If you want up to four clients, you should
+ // not go below 6kB. If your stack is too small, you will encounter
+ // Panic and stack canary exceptions, usually during the call to
+ // SSL_accept.
+ xTaskCreatePinnedToCore(serverTask, "https443", 6144, NULL, 1, NULL, ARDUINO_RUNNING_CORE);
+}
+
+void loop()
+{
+ Serial.println("loop()");
+ delay(5000);
+}
diff --git a/examples/WT32_ETH01/Async-Server/cert.h b/examples/WT32_ETH01/Async-Server/cert.h
new file mode 100644
index 0000000..46c87e4
--- /dev/null
+++ b/examples/WT32_ETH01/Async-Server/cert.h
@@ -0,0 +1,4 @@
+#ifndef CERT_H_
+#define CERT_H_
+ #error You have to run the srcipt extras/create_cert.sh to recreate these files
+#endif
diff --git a/examples/WT32_ETH01/Async-Server/private_key.h b/examples/WT32_ETH01/Async-Server/private_key.h
new file mode 100644
index 0000000..1d36c18
--- /dev/null
+++ b/examples/WT32_ETH01/Async-Server/private_key.h
@@ -0,0 +1,4 @@
+#ifndef PRIVATE_KEY_H_
+#define PRIVATE_KEY_H_
+ #error You have to run the srcipt extras/create_cert.sh to recreate these files
+#endif
diff --git a/examples/WT32_ETH01/Authentication/Authentication.ino b/examples/WT32_ETH01/Authentication/Authentication.ino
new file mode 100644
index 0000000..44daa22
--- /dev/null
+++ b/examples/WT32_ETH01/Authentication/Authentication.ino
@@ -0,0 +1,403 @@
+/**
+ Example for the WT32_ETH01 HTTP(S) Webserver
+
+ IMPORTANT NOTE:
+ To run this script, your need to
+ 1) Make sure to have certificate data available. You will find a
+ shell script (create_cert.sh) and instructions to do so in the library folder
+ under extras/
+
+ This script will install an HTTPS Server on your WT32_ETH01 with the following
+ functionalities:
+ - Show simple page on web server root
+ - Provide some "internal pages" that are protected by the server
+ - Run a middleware that authenticates the user
+ - Run a middleware that provides access control
+ - 404 for everything else
+ Authentication is done using HTTP Basic Auth, which is supported by the webserver,
+ so you don't have to care about retrieving the login information from request
+ headers.
+*/
+
+// Include certificate data (see note above)
+#include "cert.h"
+#include "private_key.h"
+
+
+//////////////////////////////////////////////////
+
+// For WT32_ETH01
+#define DEBUG_ETHERNET_WEBSERVER_PORT Serial
+
+// Debug Level from 0 to 4
+#define _ETHERNET_WEBSERVER_LOGLEVEL_ 3
+
+#include
+
+// Select the IP address according to your local network
+IPAddress myIP(192, 168, 2, 232);
+IPAddress myGW(192, 168, 2, 1);
+IPAddress mySN(255, 255, 255, 0);
+
+// Google DNS Server IP
+IPAddress myDNS(8, 8, 8, 8);
+
+//////////////////////////////////////////////////
+
+
+// For the middleware
+#include
+
+// We define two new HTTP-Header names. Those headers will be used internally
+// to store the user name and group after authentication. If the client provides
+// these headers, they will be ignored to prevent authentication bypass.
+#define HEADER_USERNAME "X-USERNAME"
+#define HEADER_GROUP "X-GROUP"
+
+// Includes for the server
+#include
+#include
+#include
+#include
+
+// The HTTPS Server comes in a separate namespace. For easier use, include it here.
+using namespace httpsserver;
+
+// Create an SSL certificate object from the files included above
+SSLCert cert = SSLCert(
+ example_crt_DER, example_crt_DER_len,
+ example_key_DER, example_key_DER_len
+ );
+
+// Create an SSL-enabled server that uses the certificate
+// The contstructor takes some more parameters, but we go for default values here.
+HTTPSServer secureServer = HTTPSServer(&cert);
+
+// Declare a middleware function.
+// Parameters:
+// req: Request data, can be used to access URL, HTTP Method, Headers, ...
+// res: Response data, can be used to access HTTP Status, Headers, ...
+// next: This function is used to pass control down the chain. If you have done your work
+// with the request object, you may decide if you want to process the request.
+// If you do so, you call the next() function, and the next middleware function (if
+// there is any) or the actual requestHandler will be called.
+// If you want to skip the request, you do not call next, and set for example status
+// code 403 on the response to show that the user is not allowed to access a specific
+// resource.
+// For more details, see the definition below.
+
+/**
+ The following middleware function is one of two functions dealing with access control. The
+ middlewareAuthentication() will interpret the HTTP Basic Auth header, check usernames and password,
+ and if they are valid, set the X-USERNAME and X-GROUP header.
+
+ If they are invalid, the X-USERNAME and X-GROUP header will be unset. This is important because
+ otherwise the client may manipulate those internal headers.
+
+ Having that done, further middleware functions and the request handler functions will be able to just
+ use req->getHeader("X-USERNAME") to find out if the user is logged in correctly.
+
+ Furthermore, if the user supplies credentials and they are invalid, he will receive an 401 response
+ without any other functions being called.
+*/
+void middlewareAuthentication(HTTPRequest * req, HTTPResponse * res, std::function next)
+{
+ // Unset both headers to discard any value from the client
+ // This prevents authentication bypass by a client that just sets X-USERNAME
+ req->setHeader(HEADER_USERNAME, "");
+ req->setHeader(HEADER_GROUP, "");
+
+ // Get login information from request
+ // If you use HTTP Basic Auth, you can retrieve the values from the request.
+ // The return values will be empty strings if the user did not provide any data,
+ // or if the format of the Authorization header is invalid (eg. no Basic Method
+ // for Authorization, or an invalid Base64 token)
+ std::string reqUsername = req->getBasicAuthUser();
+ std::string reqPassword = req->getBasicAuthPassword();
+
+ // If the user entered login information, we will check it
+ if (reqUsername.length() > 0 && reqPassword.length() > 0)
+ {
+
+ // _Very_ simple hardcoded user database to check credentials and assign the group
+ bool authValid = true;
+ std::string group = "";
+
+ if (reqUsername == "admin" && reqPassword == "secret")
+ {
+ group = "ADMIN";
+ }
+ else if (reqUsername == "user" && reqPassword == "test")
+ {
+ group = "USER";
+ }
+ else
+ {
+ authValid = false;
+ }
+
+ // If authentication was successful
+ if (authValid)
+ {
+ // set custom headers and delegate control
+ req->setHeader(HEADER_USERNAME, reqUsername);
+ req->setHeader(HEADER_GROUP, group);
+
+ // The user tried to authenticate and was successful
+ // -> We proceed with this request.
+ next();
+ }
+ else
+ {
+ // Display error page
+ res->setStatusCode(401);
+ res->setStatusText("Unauthorized");
+ res->setHeader("Content-Type", "text/plain");
+
+ // This should trigger the browser user/password dialog, and it will tell
+ // the client how it can authenticate
+ res->setHeader("WWW-Authenticate", "Basic realm=\"ESP32 privileged area\"");
+
+ // Small error text on the response document. In a real-world scenario, you
+ // shouldn't display the login information on this page, of course ;-)
+ res->println("401. Unauthorized (try admin/secret or user/test)");
+
+ // NO CALL TO next() here, as the authentication failed.
+ // -> The code above did handle the request already.
+ }
+ }
+ else
+ {
+ // No attempt to authenticate
+ // -> Let the request pass through by calling next()
+ next();
+ }
+}
+
+/**
+ This function plays together with the middlewareAuthentication(). While the first function checks the
+ username/password combination and stores it in the request, this function makes use of this information
+ to allow or deny access.
+
+ This example only prevents unauthorized access to every ResourceNode stored under an /internal/... path.
+*/
+void middlewareAuthorization(HTTPRequest * req, HTTPResponse * res, std::function next)
+{
+ // Get the username (if any)
+ std::string username = req->getHeader(HEADER_USERNAME);
+
+ // Check that only logged-in users may get to the internal area (All URLs starting with /internal)
+ // Only a simple example, more complicated configuration is up to you.
+ if (username == "" && req->getRequestString().substr(0, 9) == "/internal")
+ {
+ // Same as the deny-part in middlewareAuthentication()
+ res->setStatusCode(401);
+ res->setStatusText("Unauthorized");
+ res->setHeader("Content-Type", "text/plain");
+ res->setHeader("WWW-Authenticate", "Basic realm=\"ESP32 privileged area\"");
+ res->println("401. Unauthorized (try admin/secret or user/test)");
+
+ // No call denies access to protected handler function.
+ }
+ else
+ {
+ // Everything else will be allowed, so we call next()
+ next();
+ }
+}
+
+// This is the internal page. It will greet the user with
+// a personalized message and - if the user is in the ADMIN group -
+// provide a link to the admin interface.
+void handleInternalPage(HTTPRequest * req, HTTPResponse * res)
+{
+ // Header
+ res->setStatusCode(200);
+ res->setStatusText("OK");
+ res->setHeader("Content-Type", "text/html; charset=utf8");
+
+ // Write page
+ res->println("");
+ res->println("");
+ res->println("");
+ res->println("Internal Area");
+ res->println("");
+ res->println("");
+
+ // Personalized greeting
+ res->print("
Hello ");
+ // We can safely use the header value, this area is only accessible if it's
+ // set (the middleware takes care of this)
+ res->printStd(req->getHeader(HEADER_USERNAME));
+ res->print("!
");
+
+ res->println("
Welcome to the internal area. Congratulations on successfully entering your password!
");
+
+ // The "admin area" will only be shown if the correct group has been assigned in the authenticationMiddleware
+ if (req->getHeader(HEADER_GROUP) == "ADMIN")
+ {
+ res->println("
";
+ std::string footer = "";
+
+ // Checking permissions can not only be done centrally in the middleware function but also in the actual request handler.
+ // This would be handy if you provide an API with lists of resources, but access rights are defined object-based.
+ if (req->getHeader(HEADER_GROUP) == "ADMIN")
+ {
+ res->setStatusCode(200);
+ res->setStatusText("OK");
+ res->printStd(header);
+ res->println("
The requested resource was not found on this server.
");
+ res->println("");
+}
+
+void setup()
+{
+ // For logging
+ Serial.begin(115200);
+ while (!Serial && millis() < 5000);
+
+ ///////////////////////////////////////////////
+
+ Serial.print("\nStarting HTTP-and-HTTPS on " + String(ARDUINO_BOARD));
+ Serial.println(" with " + String(SHIELD_TYPE));
+ Serial.println(WEBSERVER_WT32_ETH01_VERSION);
+
+ // To be called before ETH.begin()
+ WT32_ETH01_onEvent();
+
+ //bool begin(uint8_t phy_addr=ETH_PHY_ADDR, int power=ETH_PHY_POWER, int mdc=ETH_PHY_MDC, int mdio=ETH_PHY_MDIO,
+ // eth_phy_type_t type=ETH_PHY_TYPE, eth_clock_mode_t clk_mode=ETH_CLK_MODE);
+ //ETH.begin(ETH_PHY_ADDR, ETH_PHY_POWER, ETH_PHY_MDC, ETH_PHY_MDIO, ETH_PHY_TYPE, ETH_CLK_MODE);
+ ETH.begin(ETH_PHY_ADDR, ETH_PHY_POWER);
+
+ // Static IP, leave without this line to get IP via DHCP
+ //bool config(IPAddress local_ip, IPAddress gateway, IPAddress subnet, IPAddress dns1 = 0, IPAddress dns2 = 0);
+ ETH.config(myIP, myGW, mySN, myDNS);
+
+ WT32_ETH01_waitForConnect();
+
+ Serial.print(F("HTTP EthernetWebServer is @ IP : "));
+ Serial.println(ETH.localIP());
+
+ ///////////////////////////////////////////////
+
+ // For every resource available on the server, we need to create a ResourceNode
+ // The ResourceNode links URL and HTTP method to a handler function
+ ResourceNode * nodeRoot = new ResourceNode("/", "GET", &handleRoot);
+ ResourceNode * nodeInternal = new ResourceNode("/internal", "GET", &handleInternalPage);
+ ResourceNode * nodeAdmin = new ResourceNode("/internal/admin", "GET", &handleAdminPage);
+ ResourceNode * nodePublic = new ResourceNode("/public", "GET", &handlePublicPage);
+ ResourceNode * node404 = new ResourceNode("", "GET", &handle404);
+
+ // Add the nodes to the server
+ secureServer.registerNode(nodeRoot);
+ secureServer.registerNode(nodeInternal);
+ secureServer.registerNode(nodeAdmin);
+ secureServer.registerNode(nodePublic);
+
+ // Add the 404 not found node to the server.
+ // The path is ignored for the default node.
+ secureServer.setDefaultNode(node404);
+
+ // Add the middleware. These functions will be called globally for every request
+ // Note: The functions are called in the order they are added to the server.
+ // This means, we need to add the authentication middleware first, because the
+ // authorization middleware needs the headers that will be set by the authentication
+ // middleware (First we check the identity, then we see what the user is allowed to do)
+ secureServer.addMiddleware(&middlewareAuthentication);
+ secureServer.addMiddleware(&middlewareAuthorization);
+
+ Serial.println("Starting server...");
+ secureServer.start();
+
+ if (secureServer.isRunning())
+ {
+ Serial.println("Server ready.");
+ }
+}
+
+void loop()
+{
+ // This call will let the server do its work
+ secureServer.loop();
+
+ // Other code would go here...
+ delay(1);
+}
diff --git a/examples/WT32_ETH01/Authentication/cert.h b/examples/WT32_ETH01/Authentication/cert.h
new file mode 100644
index 0000000..46c87e4
--- /dev/null
+++ b/examples/WT32_ETH01/Authentication/cert.h
@@ -0,0 +1,4 @@
+#ifndef CERT_H_
+#define CERT_H_
+ #error You have to run the srcipt extras/create_cert.sh to recreate these files
+#endif
diff --git a/examples/WT32_ETH01/Authentication/private_key.h b/examples/WT32_ETH01/Authentication/private_key.h
new file mode 100644
index 0000000..1d36c18
--- /dev/null
+++ b/examples/WT32_ETH01/Authentication/private_key.h
@@ -0,0 +1,4 @@
+#ifndef PRIVATE_KEY_H_
+#define PRIVATE_KEY_H_
+ #error You have to run the srcipt extras/create_cert.sh to recreate these files
+#endif
diff --git a/examples/WT32_ETH01/HTML-Forms/HTML-Forms.ino b/examples/WT32_ETH01/HTML-Forms/HTML-Forms.ino
new file mode 100644
index 0000000..e00f136
--- /dev/null
+++ b/examples/WT32_ETH01/HTML-Forms/HTML-Forms.ino
@@ -0,0 +1,487 @@
+/**
+ Example for the WT32_ETH01 HTTP(S) Webserver
+
+ IMPORTANT NOTE:
+ To run this script, you need to
+ 1) Make sure to have certificate data available. You will find a
+ shell script (create_cert.sh) and instructions to do so in the library folder
+ under extras/
+
+ This script will install an HTTPS Server on your WT32_ETH01 with the following
+ functionalities:
+ - Show simple page on web server root that includes some HTML Forms
+ - Define a POST handler that handles the forms using the HTTPBodyParser API
+ provided by the library.
+ - 404 for everything else
+*/
+
+// Include certificate data (see note above)
+#include "cert.h"
+#include "private_key.h"
+
+//////////////////////////////////////////////////
+
+// For WT32_ETH01
+#define DEBUG_ETHERNET_WEBSERVER_PORT Serial
+
+// Debug Level from 0 to 4
+#define _ETHERNET_WEBSERVER_LOGLEVEL_ 3
+
+#include
+
+// Select the IP address according to your local network
+IPAddress myIP(192, 168, 2, 232);
+IPAddress myGW(192, 168, 2, 1);
+IPAddress mySN(255, 255, 255, 0);
+
+// Google DNS Server IP
+IPAddress myDNS(8, 8, 8, 8);
+
+//////////////////////////////////////////////////
+
+// We will use SPIFFS and FS
+#include
+#include
+
+// Includes for the server
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+// We need to specify some content-type mapping, so the resources get delivered with the
+// right content type and are displayed correctly in the browser
+char contentTypes[][2][32] =
+{
+ {".txt", "text/plain"},
+ {".png", "image/png"},
+ {".jpg", "image/jpg"},
+ {"", ""}
+};
+
+// The HTTPS Server comes in a separate namespace. For easier use, include it here.
+using namespace httpsserver;
+
+// Create an SSL certificate object from the files included above
+SSLCert cert = SSLCert(
+ example_crt_DER, example_crt_DER_len,
+ example_key_DER, example_key_DER_len
+ );
+
+// Create an SSL-enabled server that uses the certificate
+// The contstructor takes some more parameters, but we go for default values here.
+HTTPSServer secureServer = HTTPSServer(&cert);
+
+// Create some handler functions for the various URLs on the server
+// The signature is always the same for those functions. They get two parameters,
+// which are pointers to the request data (read request body, headers, ...) and
+// to the response data (write response, set status code, ...)
+
+std::string htmlEncode(std::string data)
+{
+ // Quick and dirty: doesn't handle control chars and such.
+ const char *p = data.c_str();
+ std::string rv = "";
+
+ while (p && *p)
+ {
+ char escapeChar = *p++;
+
+ switch (escapeChar)
+ {
+ case '&': rv += "&"; break;
+ case '<': rv += "<"; break;
+ case '>': rv += ">"; break;
+ case '"': rv += """; break;
+ case '\'': rv += "'"; break;
+ case '/': rv += "/"; break;
+ default: rv += escapeChar; break;
+ }
+ }
+
+ return rv;
+}
+
+void handleRoot(HTTPRequest * req, HTTPResponse * res)
+{
+ // Status code is 200 OK by default.
+ // We want to deliver a simple HTML page, so we send a corresponding content type:
+ res->setHeader("Content-Type", "text/html");
+
+ // The response implements the Print interface, so you can use it just like
+ // you would write to Serial etc.
+ res->println("");
+ res->println("");
+ res->println("Very simple file server");
+ res->println("");
+ res->println("
Very simple file server
");
+ res->println("
This is a very simple file server to demonstrate the use of POST forms.
");
+ res->println("
List existing files
");
+ res->println("
See /public to list existing files and retrieve or edit them.
");
+ res->println("
Upload new file
");
+ res->println("
This form allows you to upload files (text, jpg and png supported best). It demonstrates multipart/form-data.
");
+ res->println("");
+ res->println("");
+ res->println("");
+}
+
+void handleFormUpload(HTTPRequest * req, HTTPResponse * res)
+{
+ // First, we need to check the encoding of the form that we have received.
+ // The browser will set the Content-Type request header, so we can use it for that purpose.
+ // Then we select the body parser based on the encoding.
+ // Actually we do this only for documentary purposes, we know the form is going
+ // to be multipart/form-data.
+ HTTPBodyParser *parser;
+ std::string contentType = req->getHeader("Content-Type");
+ size_t semicolonPos = contentType.find(";");
+
+ if (semicolonPos != std::string::npos)
+ {
+ contentType = contentType.substr(0, semicolonPos);
+ }
+
+ if (contentType == "multipart/form-data")
+ {
+ parser = new HTTPMultipartBodyParser(req);
+ }
+ else
+ {
+ Serial.printf("Unknown POST Content-Type: %s\n", contentType.c_str());
+
+ return;
+ }
+
+ // We iterate over the fields. Any field with a filename is uploaded
+ res->println("File Upload
File Upload
");
+ bool didwrite = false;
+
+ while (parser->nextField())
+ {
+ std::string name = parser->getFieldName();
+ std::string filename = parser->getFieldFilename();
+ std::string mimeType = parser->getFieldMimeType();
+ Serial.printf("handleFormUpload: field name='%s', filename='%s', mimetype='%s'\n", name.c_str(), filename.c_str(), mimeType.c_str());
+
+ // Double check that it is what we expect
+ if (name != "file")
+ {
+ Serial.println("Skipping unexpected field");
+ break;
+ }
+
+ // Should check file name validity and all that, but we skip that.
+ std::string pathname = "/public/" + filename;
+ File file = SPIFFS.open(pathname.c_str(), "w");
+ size_t fileLength = 0;
+ didwrite = true;
+
+ while (!parser->endOfField())
+ {
+ byte buf[512];
+ size_t readLength = parser->read(buf, 512);
+ file.write(buf, readLength);
+ fileLength += readLength;
+ }
+
+ file.close();
+ res->printf("
");
+ f = d.openNextFile();
+ }
+
+ res->println("
");
+ }
+ res->println("");
+}
+
+void handleFile(HTTPRequest * req, HTTPResponse * res)
+{
+ std::string filename = req->getRequestString();
+
+ // Check if the file exists
+ if (!SPIFFS.exists(filename.c_str()))
+ {
+ // Send "404 Not Found" as response, as the file doesn't seem to exist
+ res->setStatusCode(404);
+ res->setStatusText("Not found");
+ res->println("404 Not Found");
+
+ return;
+ }
+
+ File file = SPIFFS.open(filename.c_str());
+
+ // Set length
+ res->setHeader("Content-Length", httpsserver::intToString(file.size()));
+
+ // Content-Type is guessed using the definition of the contentTypes-table defined above
+ int cTypeIdx = 0;
+
+ do
+ {
+ if (filename.rfind(contentTypes[cTypeIdx][0]) != std::string::npos)
+ {
+ res->setHeader("Content-Type", contentTypes[cTypeIdx][1]);
+ break;
+ }
+
+ cTypeIdx += 1;
+ } while (strlen(contentTypes[cTypeIdx][0]) > 0);
+
+ // Read the file and write it to the response
+ uint8_t buffer[256];
+ size_t length = 0;
+
+ do
+ {
+ length = file.read(buffer, 256);
+ res->write(buffer, length);
+ } while (length > 0);
+
+ file.close();
+}
+
+void handle404(HTTPRequest * req, HTTPResponse * res)
+{
+ // Discard request body, if we received any
+ // We do this, as this is the default node and may also server POST/PUT requests
+ req->discardRequestBody();
+
+ // Set the response status
+ res->setStatusCode(404);
+ res->setStatusText("Not Found");
+
+ // Set content type of the response
+ res->setHeader("Content-Type", "text/html");
+
+ // Write a tiny HTML page
+ res->println("");
+ res->println("");
+ res->println("Not Found");
+ res->println("
404 Not Found
The requested resource was not found on this server.
");
+ res->println("");
+}
+
+void setup()
+{
+ // For logging
+ Serial.begin(115200);
+ while (!Serial && millis() < 5000);
+
+ ///////////////////////////////////////////////
+
+ Serial.print("\nStarting HTML_Forms on " + String(ARDUINO_BOARD));
+ Serial.println(" with " + String(SHIELD_TYPE));
+ Serial.println(WEBSERVER_WT32_ETH01_VERSION);
+
+ // To be called before ETH.begin()
+ WT32_ETH01_onEvent();
+
+ //bool begin(uint8_t phy_addr=ETH_PHY_ADDR, int power=ETH_PHY_POWER, int mdc=ETH_PHY_MDC, int mdio=ETH_PHY_MDIO,
+ // eth_phy_type_t type=ETH_PHY_TYPE, eth_clock_mode_t clk_mode=ETH_CLK_MODE);
+ //ETH.begin(ETH_PHY_ADDR, ETH_PHY_POWER, ETH_PHY_MDC, ETH_PHY_MDIO, ETH_PHY_TYPE, ETH_CLK_MODE);
+ ETH.begin(ETH_PHY_ADDR, ETH_PHY_POWER);
+
+ // Static IP, leave without this line to get IP via DHCP
+ //bool config(IPAddress local_ip, IPAddress gateway, IPAddress subnet, IPAddress dns1 = 0, IPAddress dns2 = 0);
+ ETH.config(myIP, myGW, mySN, myDNS);
+
+ WT32_ETH01_waitForConnect();
+
+ Serial.print(F("HTTP EthernetWebServer is @ IP : "));
+ Serial.println(ETH.localIP());
+
+ ///////////////////////////////////////////////
+
+ // Setup filesystem
+ if (!SPIFFS.begin(true)) Serial.println("Mounting SPIFFS failed");
+
+ // For every resource available on the server, we need to create a ResourceNode
+ // The ResourceNode links URL and HTTP method to a handler function
+ ResourceNode * nodeRoot = new ResourceNode("/", "GET", &handleRoot);
+ ResourceNode * nodeFormUpload = new ResourceNode("/upload", "POST", &handleFormUpload);
+ ResourceNode * nodeFormEdit = new ResourceNode("/edit", "GET", &handleFormEdit);
+ ResourceNode * nodeFormEditDone = new ResourceNode("/edit", "POST", &handleFormEdit);
+ ResourceNode * nodeDirectory = new ResourceNode("/public", "GET", &handleDirectory);
+ ResourceNode * nodeFile = new ResourceNode("/public/*", "GET", &handleFile);
+
+ // 404 node has no URL as it is used for all requests that don't match anything else
+ ResourceNode * node404 = new ResourceNode("", "GET", &handle404);
+
+ // Add the root nodes to the server
+ secureServer.registerNode(nodeRoot);
+ secureServer.registerNode(nodeFormUpload);
+ secureServer.registerNode(nodeFormEdit);
+ secureServer.registerNode(nodeFormEditDone);
+ secureServer.registerNode(nodeDirectory);
+ secureServer.registerNode(nodeFile);
+
+ // Add the 404 not found node to the server.
+ // The path is ignored for the default node.
+ secureServer.setDefaultNode(node404);
+
+ Serial.println("Starting server...");
+ secureServer.start();
+
+ if (secureServer.isRunning())
+ {
+ Serial.println("Server ready.");
+ }
+}
+
+void loop()
+{
+ // This call will let the server do its work
+ secureServer.loop();
+
+ // Other code would go here...
+ delay(1);
+}
diff --git a/examples/WT32_ETH01/HTML-Forms/cert.h b/examples/WT32_ETH01/HTML-Forms/cert.h
new file mode 100644
index 0000000..46c87e4
--- /dev/null
+++ b/examples/WT32_ETH01/HTML-Forms/cert.h
@@ -0,0 +1,4 @@
+#ifndef CERT_H_
+#define CERT_H_
+ #error You have to run the srcipt extras/create_cert.sh to recreate these files
+#endif
diff --git a/examples/WT32_ETH01/HTML-Forms/private_key.h b/examples/WT32_ETH01/HTML-Forms/private_key.h
new file mode 100644
index 0000000..1d36c18
--- /dev/null
+++ b/examples/WT32_ETH01/HTML-Forms/private_key.h
@@ -0,0 +1,4 @@
+#ifndef PRIVATE_KEY_H_
+#define PRIVATE_KEY_H_
+ #error You have to run the srcipt extras/create_cert.sh to recreate these files
+#endif
diff --git a/examples/WT32_ETH01/HTTPS-and-HTTP/HTTPS-and-HTTP.ino b/examples/WT32_ETH01/HTTPS-and-HTTP/HTTPS-and-HTTP.ino
new file mode 100644
index 0000000..4fd2978
--- /dev/null
+++ b/examples/WT32_ETH01/HTTPS-and-HTTP/HTTPS-and-HTTP.ino
@@ -0,0 +1,176 @@
+/**
+ Example for the WT32_ETH01 HTTP(S) Webserver
+
+ IMPORTANT NOTE:
+ To run this script, your need to
+ 1) Make sure to have certificate data available. You will find a
+ shell script (create_cert.sh) and instructions to do so in the library folder
+ under extras/
+
+ This script will install an HTTP Server on port 80 and an HTTPS server
+ on port 443 of your WT32_ETH01 with the following functionalities:
+ - Show simple page on web server root
+ - 404 for everything else
+
+ The comments in this script focus on making both protocols available,
+ for setting up the server itself, see Static-Page.
+*/
+
+// Include certificate data (see note above)
+#include "cert.h"
+#include "private_key.h"
+
+//////////////////////////////////////////////////
+
+// For WT32_ETH01
+#define DEBUG_ETHERNET_WEBSERVER_PORT Serial
+
+// Debug Level from 0 to 4
+#define _ETHERNET_WEBSERVER_LOGLEVEL_ 3
+
+#include
+
+// Select the IP address according to your local network
+IPAddress myIP(192, 168, 2, 232);
+IPAddress myGW(192, 168, 2, 1);
+IPAddress mySN(255, 255, 255, 0);
+
+// Google DNS Server IP
+IPAddress myDNS(8, 8, 8, 8);
+
+//////////////////////////////////////////////////
+
+// Includes for the server
+// Note: We include HTTPServer and HTTPSServer
+#include
+#include
+#include
+#include
+#include
+
+// The HTTPS Server comes in a separate namespace. For easier use, include it here.
+using namespace httpsserver;
+
+// Create an SSL certificate object from the files included above
+SSLCert cert = SSLCert(
+ example_crt_DER, example_crt_DER_len,
+ example_key_DER, example_key_DER_len
+ );
+
+// First, we create the HTTPSServer with the certificate created above
+HTTPSServer secureServer = HTTPSServer(&cert);
+
+// Additionally, we create an HTTPServer for unencrypted traffic
+HTTPServer insecureServer = HTTPServer();
+
+// The hanlder functions are the same as in the Static-Page example.
+// The only difference is the check for isSecure in the root handler
+
+void handleRoot(HTTPRequest * req, HTTPResponse * res)
+{
+ res->setHeader("Content-Type", "text/html");
+
+ res->println("");
+ res->println("");
+ res->println("Hello World!");
+ res->println("");
+ res->println("
Hello World!
");
+
+ res->print("
Your server is running for ");
+ res->print((int)(millis() / 1000), DEC);
+ res->println(" seconds.
");
+
+ // You can check if you are connected over a secure connection, eg. if you
+ // want to use authentication and redirect the user to a secure connection
+ // for that
+ if (req->isSecure())
+ {
+ res->println("
The requested resource was not found on this server.
");
+ res->println("");
+}
+
+void setup()
+{
+ // For logging
+ Serial.begin(115200);
+ while (!Serial && millis() < 5000);
+
+ ///////////////////////////////////////////////
+
+ Serial.print("\nStarting HTTP-and-HTTPS on " + String(ARDUINO_BOARD));
+ Serial.println(" with " + String(SHIELD_TYPE));
+ Serial.println(WEBSERVER_WT32_ETH01_VERSION);
+
+ // To be called before ETH.begin()
+ WT32_ETH01_onEvent();
+
+ //bool begin(uint8_t phy_addr=ETH_PHY_ADDR, int power=ETH_PHY_POWER, int mdc=ETH_PHY_MDC, int mdio=ETH_PHY_MDIO,
+ // eth_phy_type_t type=ETH_PHY_TYPE, eth_clock_mode_t clk_mode=ETH_CLK_MODE);
+ //ETH.begin(ETH_PHY_ADDR, ETH_PHY_POWER, ETH_PHY_MDC, ETH_PHY_MDIO, ETH_PHY_TYPE, ETH_CLK_MODE);
+ ETH.begin(ETH_PHY_ADDR, ETH_PHY_POWER);
+
+ // Static IP, leave without this line to get IP via DHCP
+ //bool config(IPAddress local_ip, IPAddress gateway, IPAddress subnet, IPAddress dns1 = 0, IPAddress dns2 = 0);
+ ETH.config(myIP, myGW, mySN, myDNS);
+
+ WT32_ETH01_waitForConnect();
+
+ Serial.print(F("HTTP EthernetWebServer is @ IP : "));
+ Serial.println(ETH.localIP());
+
+ ///////////////////////////////////////////////
+
+ // For every resource available on the server, we need to create a ResourceNode
+ // The ResourceNode links URL and HTTP method to a handler function
+ ResourceNode * nodeRoot = new ResourceNode("/", "GET", &handleRoot);
+ ResourceNode * node404 = new ResourceNode("", "GET", &handle404);
+
+ // Add the root node to the servers. We can use the same ResourceNode on multiple
+ // servers (you could also run multiple HTTPS servers)
+ secureServer.registerNode(nodeRoot);
+ insecureServer.registerNode(nodeRoot);
+
+ // We do the same for the default Node
+ secureServer.setDefaultNode(node404);
+ insecureServer.setDefaultNode(node404);
+
+ Serial.println("Starting HTTPS server...");
+ secureServer.start();
+ Serial.println("Starting HTTP server...");
+ insecureServer.start();
+
+ if (secureServer.isRunning() && insecureServer.isRunning())
+ {
+ Serial.println("Servers ready.");
+ }
+}
+
+void loop()
+{
+ // We need to call both loop functions here
+ secureServer.loop();
+ insecureServer.loop();
+
+ // Other code would go here...
+ delay(1);
+}
diff --git a/examples/WT32_ETH01/HTTPS-and-HTTP/cert.h b/examples/WT32_ETH01/HTTPS-and-HTTP/cert.h
new file mode 100644
index 0000000..46c87e4
--- /dev/null
+++ b/examples/WT32_ETH01/HTTPS-and-HTTP/cert.h
@@ -0,0 +1,4 @@
+#ifndef CERT_H_
+#define CERT_H_
+ #error You have to run the srcipt extras/create_cert.sh to recreate these files
+#endif
diff --git a/examples/WT32_ETH01/HTTPS-and-HTTP/private_key.h b/examples/WT32_ETH01/HTTPS-and-HTTP/private_key.h
new file mode 100644
index 0000000..1d36c18
--- /dev/null
+++ b/examples/WT32_ETH01/HTTPS-and-HTTP/private_key.h
@@ -0,0 +1,4 @@
+#ifndef PRIVATE_KEY_H_
+#define PRIVATE_KEY_H_
+ #error You have to run the srcipt extras/create_cert.sh to recreate these files
+#endif
diff --git a/examples/WT32_ETH01/Middleware/Middleware.ino b/examples/WT32_ETH01/Middleware/Middleware.ino
new file mode 100644
index 0000000..c1b02d1
--- /dev/null
+++ b/examples/WT32_ETH01/Middleware/Middleware.ino
@@ -0,0 +1,188 @@
+/**
+ Example for the WT32_ETH01 HTTP(S) Webserver
+
+ IMPORTANT NOTE:
+ To run this script, your need to
+ 1) Make sure to have certificate data available. You will find a
+ shell script (create_cert.sh) and instructions to do so in the library folder
+ under extras/
+
+ This script will install an HTTPS Server on your WT32_ETH01 with the following
+ functionalities:
+ - Show simple page on web server root
+ - Run a middleware that logs every request
+ - 404 for everything else
+*/
+
+// Include certificate data (see note above)
+#include "cert.h"
+#include "private_key.h"
+
+//////////////////////////////////////////////////
+
+// For WT32_ETH01
+#define DEBUG_ETHERNET_WEBSERVER_PORT Serial
+
+// Debug Level from 0 to 4
+#define _ETHERNET_WEBSERVER_LOGLEVEL_ 3
+
+#include
+
+// Select the IP address according to your local network
+IPAddress myIP(192, 168, 2, 232);
+IPAddress myGW(192, 168, 2, 1);
+IPAddress mySN(255, 255, 255, 0);
+
+// Google DNS Server IP
+IPAddress myDNS(8, 8, 8, 8);
+
+//////////////////////////////////////////////////
+
+// For the middleware
+#include
+
+// Includes for the server
+#include
+#include
+#include
+#include
+
+// The HTTPS Server comes in a separate namespace. For easier use, include it here.
+using namespace httpsserver;
+
+// Create an SSL certificate object from the files included above
+SSLCert cert = SSLCert(
+ example_crt_DER, example_crt_DER_len,
+ example_key_DER, example_key_DER_len
+ );
+
+// Create an SSL-enabled server that uses the certificate
+// The contstructor takes some more parameters, but we go for default values here.
+HTTPSServer secureServer = HTTPSServer(&cert);
+
+// Declare a middleware function.
+// Parameters:
+// req: Request data, can be used to access URL, HTTP Method, Headers, ...
+// res: Response data, can be used to access HTTP Status, Headers, ...
+// next: This function is used to pass control down the chain. If you have done your work
+// with the request object, you may decide if you want to process the request.
+// If you do so, you call the next() function, and the next middleware function (if
+// there is any) or the actual requestHandler will be called.
+// If you want to skip the request, you do not call next, and set for example status
+// code 403 on the response to show that the user is not allowed to access a specific
+// resource.
+// The Authentication examples provides more details on this.
+// We want to log the following information for every request:
+// - Response Status
+// - Request Method
+// - Request String (URL + Parameters)
+void middlewareLogging(HTTPRequest * req, HTTPResponse * res, std::function next)
+{
+ // We want to print the response status, so we need to call next() first.
+ next();
+ // After the call, the status is (hopefully) set by the handler function, so we can
+ // access it for logging.
+ Serial.printf("middlewareLogging(): %3d\t%s\t\t%s\n",
+ // Status code (like: 200)
+ res->getStatusCode(),
+ // Method used for the request (like: GET)
+ req->getMethod().c_str(),
+ // Request string (like /index.html)
+ req->getRequestString().c_str());
+}
+
+// For details on the implementation of the hanlder functions, refer to the Static-Page example.
+void handleRoot(HTTPRequest * req, HTTPResponse * res)
+{
+ res->setHeader("Content-Type", "text/html");
+ res->println("");
+ res->println("");
+ res->println("Hello World!");
+ res->println("");
+ res->println("
Hello World!
");
+ res->print("
Your server is running for ");
+ res->print((int)(millis() / 1000), DEC);
+ res->println(" seconds.
The requested resource was not found on this server.
");
+ res->println("");
+}
+
+void setup()
+{
+ // For logging
+ Serial.begin(115200);
+ while (!Serial && millis() < 5000);
+
+ ///////////////////////////////////////////////
+
+ Serial.print("\nStarting Middleware on " + String(ARDUINO_BOARD));
+ Serial.println(" with " + String(SHIELD_TYPE));
+ Serial.println(WEBSERVER_WT32_ETH01_VERSION);
+
+ // To be called before ETH.begin()
+ WT32_ETH01_onEvent();
+
+ //bool begin(uint8_t phy_addr=ETH_PHY_ADDR, int power=ETH_PHY_POWER, int mdc=ETH_PHY_MDC, int mdio=ETH_PHY_MDIO,
+ // eth_phy_type_t type=ETH_PHY_TYPE, eth_clock_mode_t clk_mode=ETH_CLK_MODE);
+ //ETH.begin(ETH_PHY_ADDR, ETH_PHY_POWER, ETH_PHY_MDC, ETH_PHY_MDIO, ETH_PHY_TYPE, ETH_CLK_MODE);
+ ETH.begin(ETH_PHY_ADDR, ETH_PHY_POWER);
+
+ // Static IP, leave without this line to get IP via DHCP
+ //bool config(IPAddress local_ip, IPAddress gateway, IPAddress subnet, IPAddress dns1 = 0, IPAddress dns2 = 0);
+ ETH.config(myIP, myGW, mySN, myDNS);
+
+ WT32_ETH01_waitForConnect();
+
+ Serial.print(F("HTTP EthernetWebServer is @ IP : "));
+ Serial.println(ETH.localIP());
+
+ ///////////////////////////////////////////////
+
+ // For every resource available on the server, we need to create a ResourceNode
+ // The ResourceNode links URL and HTTP method to a handler function
+ ResourceNode * nodeRoot = new ResourceNode("/", "GET", &handleRoot);
+ ResourceNode * node404 = new ResourceNode("", "GET", &handle404);
+
+ // Add the root node to the server
+ secureServer.registerNode(nodeRoot);
+
+ // Add the 404 not found node to the server.
+ // The path is ignored for the default node.
+ secureServer.setDefaultNode(node404);
+
+ // Add the middleware. The function will be called globally for every request
+ // Note: The functions are called in the order they are added to the server.
+ // Also, if you want a middleware to handle only specific requests, you can check
+ // the URL within the middleware function.
+ secureServer.addMiddleware(&middlewareLogging);
+
+ Serial.println("Starting server...");
+ secureServer.start();
+
+ if (secureServer.isRunning())
+ {
+ Serial.println("Server ready.");
+ }
+}
+
+void loop()
+{
+ // This call will let the server do its work
+ secureServer.loop();
+
+ // Other code would go here...
+ delay(1);
+}
diff --git a/examples/WT32_ETH01/Middleware/cert.h b/examples/WT32_ETH01/Middleware/cert.h
new file mode 100644
index 0000000..46c87e4
--- /dev/null
+++ b/examples/WT32_ETH01/Middleware/cert.h
@@ -0,0 +1,4 @@
+#ifndef CERT_H_
+#define CERT_H_
+ #error You have to run the srcipt extras/create_cert.sh to recreate these files
+#endif
diff --git a/examples/WT32_ETH01/Middleware/private_key.h b/examples/WT32_ETH01/Middleware/private_key.h
new file mode 100644
index 0000000..1d36c18
--- /dev/null
+++ b/examples/WT32_ETH01/Middleware/private_key.h
@@ -0,0 +1,4 @@
+#ifndef PRIVATE_KEY_H_
+#define PRIVATE_KEY_H_
+ #error You have to run the srcipt extras/create_cert.sh to recreate these files
+#endif
diff --git a/examples/WT32_ETH01/Parameter-Validation/Parameter-Validation.ino b/examples/WT32_ETH01/Parameter-Validation/Parameter-Validation.ino
new file mode 100644
index 0000000..40ea049
--- /dev/null
+++ b/examples/WT32_ETH01/Parameter-Validation/Parameter-Validation.ino
@@ -0,0 +1,312 @@
+/**
+ Example for the WT32_ETH01 HTTP(S) Webserver
+
+ IMPORTANT NOTE:
+ To run this script, you need to
+ 1) Make sure to have certificate data available. You will find a
+ shell script (create_cert.sh) and instructions to do so in the library folder
+ under extras/
+
+ This script will install an HTTPS Server on your WT32_ETH01 with the following
+ functionalities:
+ - Shows you a page with some LEDs and allow you to turn them on or off
+ Parameters for the URLs are checked, so that you cannot address non-existing objects
+ - 404 for everything else
+ If you want to see the LEDs, connect them to GPIOs 33 (red), 25 (yellow), 26 (green)
+ and 27 (blue).
+*/
+
+// Include certificate data (see note above)
+#include "cert.h"
+#include "private_key.h"
+
+
+//////////////////////////////////////////////////
+
+// For WT32_ETH01
+#define DEBUG_ETHERNET_WEBSERVER_PORT Serial
+
+// Debug Level from 0 to 4
+#define _ETHERNET_WEBSERVER_LOGLEVEL_ 3
+
+#include
+
+// Select the IP address according to your local network
+IPAddress myIP(192, 168, 2, 232);
+IPAddress myGW(192, 168, 2, 1);
+IPAddress mySN(255, 255, 255, 0);
+
+// Google DNS Server IP
+IPAddress myDNS(8, 8, 8, 8);
+
+//////////////////////////////////////////////////
+
+// We use strings
+#include
+
+// Includes for the server
+#include
+#include
+#include
+#include
+#include
+
+// The HTTPS Server comes in a separate namespace. For easier use, include it here.
+using namespace httpsserver;
+
+// Create an SSL certificate object from the files included above
+SSLCert cert = SSLCert(
+ example_crt_DER, example_crt_DER_len,
+ example_key_DER, example_key_DER_len
+ );
+
+// Create an SSL-enabled server that uses the certificate
+// The contstructor takes some more parameters, but we go for default values here.
+HTTPSServer secureServer = HTTPSServer(&cert);
+
+// A class that defines an LED
+class LED
+{
+ public:
+ /** Name for the LED */
+ const std::string _name;
+ /** Pin that it's connected to */
+ const uint8_t _pin;
+ /** Current state */
+ bool _on;
+ /** Constructor */
+ LED(const std::string name, uint8_t pin): _name(name), _pin(pin)
+ {
+ _on = false;
+ pinMode(pin, OUTPUT);
+ }
+
+ /** Method to turn the led on or of */
+ void setOn(bool on)
+ {
+ digitalWrite(_pin, on ? HIGH : LOW);
+ _on = on;
+ }
+};
+
+// We create some LEDs:
+#define LEDCOUNT 4
+LED myLEDs[LEDCOUNT] =
+{
+ LED("Red LED", 33),
+ LED("Yellow LED", 25),
+ LED("Green LED", 26),
+ LED("Blue LED", 27)
+};
+
+// Root node, will show the LEDs that are available
+void handleRoot(HTTPRequest * req, HTTPResponse * res)
+{
+ // We will deliver an HTML page
+ res->setHeader("Content-Type", "text/html");
+
+ // Write the response page
+ res->println("");
+ res->println("Parameter Validation Example");
+ res->println("");
+
+ // Iterate over the LEDs.
+ for (int id = 0; id < LEDCOUNT; id++)
+ {
+ LED * led = &myLEDs[id];
+
+ res->print(
+ "");
+ }
+
+ res->print(
+ ""
+ ""
+ ""
+ );
+}
+
+// Node to switch an LED on or off
+// This is the handler for our post callback. We can work with the parameters without further
+// validation, as the server assures this function only gets called if the validation succeeded.
+void handleSwitch(HTTPRequest * req, HTTPResponse * res)
+{
+ // POST, so drain the input, if any
+ req->discardRequestBody();
+
+ // Get access to the parameters
+ ResourceParameters * params = req->getParams();
+
+ // Get the LED that is requested.
+ // Note that we can call stoi safely without further validation, as we
+ // defined that is has to be an unsigned integer and must not be >LEDCOUNT-1
+ LED * led = &myLEDs[std::stoi(params->getPathParameter(0))];
+
+ // Set the state of the LED. The value of the parameter can only be "0" or "1" here,
+ // otherwise the server would not have called the handler.
+ led->setOn(params->getPathParameter(1) != "0");
+
+ // Redirect the user to the main page
+ res->setStatusCode(303);
+ // This should make the browser do a GET /
+ res->setStatusText("See Other");
+ res->setHeader("Location", "/");
+ res->println("Redirecting...");
+}
+
+// Validation function for the LED ID and state
+// This function is the validator for the second parameter of the POST /led route.
+// It accepts only the strings "0" (off) and "1" (on)
+bool validateLEDState(std::string s)
+{
+ return s == "0" || s == "1";
+}
+
+// This function is a validator for the first parameter of the POST /led route.
+// We did check before that the parameter is an integer, now we check its range.
+bool validateLEDID(std::string s)
+{
+ uint32_t id = std::stoul(s);
+
+ return id < LEDCOUNT;
+}
+
+// Default handler for resources that do not exist
+// For details to this function, see the Static-Page example
+void handle404(HTTPRequest * req, HTTPResponse * res)
+{
+ req->discardRequestBody();
+ res->setStatusCode(404);
+ res->setStatusText("Not Found");
+ res->setHeader("Content-Type", "text/html");
+ res->println("");
+ res->println("");
+ res->println("Not Found");
+ res->println("
404 Not Found
The requested resource was not found on this server.
");
+ res->println("");
+}
+
+void setup()
+{
+ // For logging
+ Serial.begin(115200);
+ while (!Serial && millis() < 5000);
+
+ ///////////////////////////////////////////////
+
+ Serial.print("\nStarting Parameter_Validation on " + String(ARDUINO_BOARD));
+ Serial.println(" with " + String(SHIELD_TYPE));
+ Serial.println(WEBSERVER_WT32_ETH01_VERSION);
+
+ // To be called before ETH.begin()
+ WT32_ETH01_onEvent();
+
+ //bool begin(uint8_t phy_addr=ETH_PHY_ADDR, int power=ETH_PHY_POWER, int mdc=ETH_PHY_MDC, int mdio=ETH_PHY_MDIO,
+ // eth_phy_type_t type=ETH_PHY_TYPE, eth_clock_mode_t clk_mode=ETH_CLK_MODE);
+ //ETH.begin(ETH_PHY_ADDR, ETH_PHY_POWER, ETH_PHY_MDC, ETH_PHY_MDIO, ETH_PHY_TYPE, ETH_CLK_MODE);
+ ETH.begin(ETH_PHY_ADDR, ETH_PHY_POWER);
+
+ // Static IP, leave without this line to get IP via DHCP
+ //bool config(IPAddress local_ip, IPAddress gateway, IPAddress subnet, IPAddress dns1 = 0, IPAddress dns2 = 0);
+ ETH.config(myIP, myGW, mySN, myDNS);
+
+ WT32_ETH01_waitForConnect();
+
+ Serial.print(F("HTTP EthernetWebServer is @ IP : "));
+ Serial.println(ETH.localIP());
+
+ ///////////////////////////////////////////////
+
+ // We create a node for the main page of the server, available via get
+ ResourceNode * nodeRoot = new ResourceNode("/", "GET", &handleRoot);
+
+ // This node will turn an LED on or of. It has two parameters:
+ // 1) The ID of the LED (0..LEDCOUNT)
+ // 2) The new state (0..1)
+ // For more information on path parameters in general, see the Parameters example.
+ ResourceNode * nodeSwitch = new ResourceNode("/led/*/*", "POST", &handleSwitch);
+
+ // We want to use parameter validation. The ResourceNode class provides the method
+ // addPathParamValidator() for that. This method takes two parameters:
+ // 1) The index of the parameter that you want to validate, so for the first wildcard
+ // in the route pattern that has been specified above, it's 0, and for the second
+ // parameter it's 1.
+ // 2) A function pointer that takes an std::string as parameter and returns a bool.
+ // That bool should be true if the parameter is considered valid.
+ // All those functions are called in the order in that they have been added. So if
+ // you want check if a parameter is an integer and then do some calculation with it,
+ // make sure to add the integer-check first and the other function later.
+ //
+ // If any of the functions returns false, the URL is considered to be invalid completely.
+ // In this case, the server will return with a static 400 Bad Request response.
+ //
+ // For convenience, the ValidatorFunctions.hpp include file already provides some useful
+ // and common checks (integer, non-empty, ...). Have a look at it before you start
+ // implementing your own checks to save time!
+
+ // First we will take care of the LED ID. This ID should be...
+ // ... an unsigned integer ...
+ nodeSwitch->addPathParamValidator(0, &validateUnsignedInteger);
+ // ... and within the range of known IDs.
+ // We can treat the parameter safely as integer in this validator, as all validators
+ // are executed in order and validateUnsignedInteger has been run before.
+ nodeSwitch->addPathParamValidator(0, &validateLEDID);
+
+ // The second parameter should either be 0 or 1. We use our custom validateLEDState() validator for this:
+ nodeSwitch->addPathParamValidator(1, &validateLEDState);
+
+ // Not found node
+ ResourceNode * node404 = new ResourceNode("", "GET", &handle404);
+
+ // Add the root node to the server
+ secureServer.registerNode(nodeRoot);
+ // And the switch node
+ secureServer.registerNode(nodeSwitch);
+
+ // Add the 404 not found node to the server.
+ // The path is ignored for the default node.
+ secureServer.setDefaultNode(node404);
+
+ Serial.println("Starting server...");
+ secureServer.start();
+
+ if (secureServer.isRunning())
+ {
+ Serial.println("Server ready.");
+ }
+}
+
+void loop()
+{
+ // This call will let the server do its work
+ secureServer.loop();
+
+ // Other code would go here...
+ delay(1);
+}
diff --git a/examples/WT32_ETH01/Parameter-Validation/cert.h b/examples/WT32_ETH01/Parameter-Validation/cert.h
new file mode 100644
index 0000000..46c87e4
--- /dev/null
+++ b/examples/WT32_ETH01/Parameter-Validation/cert.h
@@ -0,0 +1,4 @@
+#ifndef CERT_H_
+#define CERT_H_
+ #error You have to run the srcipt extras/create_cert.sh to recreate these files
+#endif
diff --git a/examples/WT32_ETH01/Parameter-Validation/private_key.h b/examples/WT32_ETH01/Parameter-Validation/private_key.h
new file mode 100644
index 0000000..1d36c18
--- /dev/null
+++ b/examples/WT32_ETH01/Parameter-Validation/private_key.h
@@ -0,0 +1,4 @@
+#ifndef PRIVATE_KEY_H_
+#define PRIVATE_KEY_H_
+ #error You have to run the srcipt extras/create_cert.sh to recreate these files
+#endif
diff --git a/examples/WT32_ETH01/Parameters/Parameters.ino b/examples/WT32_ETH01/Parameters/Parameters.ino
new file mode 100644
index 0000000..1af89cc
--- /dev/null
+++ b/examples/WT32_ETH01/Parameters/Parameters.ino
@@ -0,0 +1,409 @@
+/**
+ Example for the WT32_ETH01 HTTP(S) Webserver
+
+ IMPORTANT NOTE:
+ To run this script, your need to
+ 1) Make sure to have certificate data available. You will find a
+ shell script (create_cert.sh) and instructions to do so in the library folder
+ under extras/
+
+ This script will install an HTTPS Server on your WT32_ETH01 with the following
+ functionalities:
+ - Show page on web server root that will display some SVG smileys.
+ Using a simple HTML form and GET parameters, you can change the colors
+ of those images.
+ - Provide the svg image with an optional GET parameter that allows to
+ modify the background color, URL: /images/awesome.svg
+ - Provide an example that shows how wildcard URL parameters get parsed
+ URL: /urlparam/some/thing
+ - 404 for everything else
+*/
+
+// Include certificate data (see note above)
+#include "cert.h"
+#include "private_key.h"
+
+//////////////////////////////////////////////////
+
+// For WT32_ETH01
+#define DEBUG_ETHERNET_WEBSERVER_PORT Serial
+
+// Debug Level from 0 to 4
+#define _ETHERNET_WEBSERVER_LOGLEVEL_ 3
+
+#include
+
+// Select the IP address according to your local network
+IPAddress myIP(192, 168, 2, 232);
+IPAddress myGW(192, 168, 2, 1);
+IPAddress mySN(255, 255, 255, 0);
+
+// Google DNS Server IP
+IPAddress myDNS(8, 8, 8, 8);
+
+//////////////////////////////////////////////////
+
+// Includes for the server
+#include
+#include
+#include
+#include
+
+// The HTTPS Server comes in a separate namespace. For easier use, include it here.
+using namespace httpsserver;
+
+// Create an SSL certificate object from the files included above
+SSLCert cert = SSLCert(
+ example_crt_DER, example_crt_DER_len,
+ example_key_DER, example_key_DER_len
+ );
+
+// Create an SSL-enabled server that uses the certificate
+// The contstructor takes some more parameters, but we go for default values here.
+HTTPSServer secureServer = HTTPSServer(&cert);
+
+// Handler functions
+
+void handleRoot(HTTPRequest * req, HTTPResponse * res)
+{
+ // We will deliver an HTML page
+ res->setHeader("Content-Type", "text/html");
+
+ // Write the response page
+ res->println("");
+ res->println("");
+ res->println("Hello World!");
+ res->println("");
+ res->println("");
+
+ res->println("
Query Parameters
");
+ res->println("
The parameters after the question mark in your URL.
");
+
+ // Show a form to select a color to colorize the faces
+ // We pass the selection as get parameter "shades" to this very same page,
+ // so we can evaluate it below
+ res->println("");
+
+ // Get the params to check if the user did select something
+ ResourceParameters * params = req->getParams();
+ std::string paramName = "shades";
+
+ // Print 6 faces
+ for (int i = 0; i < 6; i++)
+ {
+ // Include the image of the handleSVG function with a specific color code
+ res->print("getQueryParameter(paramName, paramVal))
+ {
+ if (paramVal == "red" || paramVal == "magenta" || paramVal == "yellow" || paramVal == "rainbow")
+ {
+ r = 128 + random(0, 128);
+ }
+
+ if (paramVal == "green" || paramVal == "cyan" || paramVal == "yellow" || paramVal == "rainbow")
+ {
+ g = 128 + random(0, 128);
+ }
+
+ if (paramVal == "blue" || paramVal == "magenta" || paramVal == "cyan" || paramVal == "rainbow")
+ {
+ b = 128 + random(0, 128);
+ }
+ }
+
+ // Print the random color. As the HTTPResponse extends the Print interface, we can make use of that.
+ res->print(r, HEX);
+ res->print(g, HEX);
+ res->print(b, HEX);
+
+ res->print("\" alt=\"Awesome!\" />");
+ }
+
+ res->println("
");
+
+ res->println("");
+ res->println("");
+}
+
+// This callback responds with an SVG image to a GET request. The icon is the "awesome face".
+// (borrowed from https://commons.wikimedia.org/wiki/File:718smiley.svg)
+//
+// If the color query parameter is set (so the URL is like awesome.svg?color=fede58), the
+// background of our awesome face is changed.
+void handleSVG(HTTPRequest * req, HTTPResponse * res)
+{
+ // Get access to the parameters
+ ResourceParameters * params = req->getParams();
+
+ // Set SVG content type
+ res->setHeader("Content-Type", "image/svg+xml");
+
+ // Set a default color
+ std::string fillColor = "fede58";
+
+ // Get request parameter (like awesome.svg?color=ff0000) and validate it
+ std::string colorParamName = "color";
+
+ // Check that the parameter is set and retrieve it.
+ // The getQueryParameter function will modify the second parameter, but only if the query
+ // parameter is set.
+ std::string requestColor;
+
+ if (params->getQueryParameter(colorParamName, requestColor))
+ {
+ // Check for correct length
+ if (requestColor.length() == 6)
+ {
+ bool colorOk = true;
+
+ // Check that we only have characters within [0-9a-fA-F]
+ for (int i = 1; i < 6 && colorOk; i++)
+ {
+ if (!(
+ (requestColor[i] >= '0' && requestColor[i] <= '9' ) ||
+ (requestColor[i] >= 'a' && requestColor[i] <= 'f' ) ||
+ (requestColor[i] >= 'A' && requestColor[i] <= 'F' )
+ ))
+ {
+ colorOk = false;
+ }
+ }
+
+ // If validation was successful, replace the default color
+ if (colorOk)
+ {
+ fillColor = requestColor;
+ }
+ }
+ }
+
+ // Print the SVG to the response:
+ res->print("");
+ res->print("");
+}
+
+// This is a more generic demo for the query parameters. It makes use of the iterator
+// interface to access them, which is useful if you do not know the paramter names in
+// adavance.
+void handleQueryDemo(HTTPRequest * req, HTTPResponse * res)
+{
+ // A word of warning: In this example, we use the query parameters and directly print
+ // them into the HTML output. We do this to simplify the demo. NEVER do this in a
+ // real application, as it allows cross-site-scripting.
+ res->setHeader("Content-Type", "text/html");
+
+ res->println("");
+ res->println("");
+ res->println("");
+ res->println("Query Parameter Demo");
+ res->println("");
+ res->println("");
+ res->println("
The following query paramters have been set:
");
+
+ // Start a table to display the parameters
+ res->println("
");
+ res->println("
Key
Value
");
+ // Iterate over the parameters. For more information, read about the C++ standard template library,
+ // especially about vectors and iterators.
+ ResourceParameters *params = req->getParams();
+
+ for (auto it = params->beginQueryParameters(); it != params->endQueryParameters(); ++it)
+ {
+ res->print("
");
+
+ // The iterator yields std::pairs of std::strings. The first value contains the parameter key
+ res->printStd((*it).first);
+ res->print("
");
+
+ // and the second value contains the parameter value
+ res->printStd((*it).second);
+ res->println("
");
+ }
+
+ res->println("
");
+
+ // You can retrieve the total parameter count from the parameters instance:
+ res->print("
There are a total of ");
+ res->print(params->getQueryParameterCount());
+ res->print(" parameters, with ");
+ res->print(params->getQueryParameterCount(true));
+ res->println(" unique keys.
");
+ res->println("");
+ res->println("");
+}
+
+// This is a simple handler function that will show the content of URL parameters.
+// If you call for example /urlparam/foo/bar, you will get the parameter values
+// "foo" and "bar" provided by the ResourceParameters.
+void handlePathParam(HTTPRequest * req, HTTPResponse * res)
+{
+ // Get access to the parameters
+ ResourceParameters * params = req->getParams();
+
+ // Set a simple content type
+ res->setHeader("Content-Type", "text/plain");
+
+ // The url pattern is: /urlparam/*/*
+ // This will make the content for the first parameter available on index 0,
+ // and the second wildcard as index 1.
+ // getPathParameter will - like getQueryParameter - write the value to the second parameter,
+ // and return true, if the index is valid. Otherwise it returns false and leaves the second
+ // parameter as it is.
+
+ std::string parameter1, parameter2;
+
+ // Print the first parameter value
+ if (params->getPathParameter(0, parameter1))
+ {
+ res->print("Parameter 1: ");
+ res->printStd(parameter1);
+ }
+
+ res->println();
+
+ // Print the second parameter value
+ if (params->getPathParameter(1, parameter2))
+ {
+ res->print("Parameter 2: ");
+ res->printStd(parameter2);
+ }
+
+ res->println("\n\nChange the parameters in the URL to see how they get parsed!");
+}
+
+// For details to this function, see the Static-Page example
+void handle404(HTTPRequest * req, HTTPResponse * res)
+{
+ req->discardRequestBody();
+ res->setStatusCode(404);
+ res->setStatusText("Not Found");
+ res->setHeader("Content-Type", "text/html");
+ res->println("");
+ res->println("");
+ res->println("Not Found");
+ res->println("
404 Not Found
The requested resource was not found on this server.
");
+ res->println("");
+}
+
+void setup()
+{
+ // For logging
+ Serial.begin(115200);
+ while (!Serial && millis() < 5000);
+
+ ///////////////////////////////////////////////
+
+ Serial.print("\nStarting Parameter on " + String(ARDUINO_BOARD));
+ Serial.println(" with " + String(SHIELD_TYPE));
+ Serial.println(WEBSERVER_WT32_ETH01_VERSION);
+
+ // To be called before ETH.begin()
+ WT32_ETH01_onEvent();
+
+ //bool begin(uint8_t phy_addr=ETH_PHY_ADDR, int power=ETH_PHY_POWER, int mdc=ETH_PHY_MDC, int mdio=ETH_PHY_MDIO,
+ // eth_phy_type_t type=ETH_PHY_TYPE, eth_clock_mode_t clk_mode=ETH_CLK_MODE);
+ //ETH.begin(ETH_PHY_ADDR, ETH_PHY_POWER, ETH_PHY_MDC, ETH_PHY_MDIO, ETH_PHY_TYPE, ETH_CLK_MODE);
+ ETH.begin(ETH_PHY_ADDR, ETH_PHY_POWER);
+
+ // Static IP, leave without this line to get IP via DHCP
+ //bool config(IPAddress local_ip, IPAddress gateway, IPAddress subnet, IPAddress dns1 = 0, IPAddress dns2 = 0);
+ ETH.config(myIP, myGW, mySN, myDNS);
+
+ WT32_ETH01_waitForConnect();
+
+ Serial.print(F("HTTP EthernetWebServer is @ IP : "));
+ Serial.println(ETH.localIP());
+
+ ///////////////////////////////////////////////
+
+ // For every resource available on the server, we need to create a ResourceNode
+ // The ResourceNode links URL and HTTP method to a handler function
+ ResourceNode * nodeRoot = new ResourceNode("/", "GET", &handleRoot);
+ ResourceNode * nodeSVG = new ResourceNode("/images/awesome.svg", "GET", &handleSVG);
+ ResourceNode * nodeQueryDemo = new ResourceNode("/queryparams", "GET", &handleQueryDemo);
+ ResourceNode * node404 = new ResourceNode("", "GET", &handle404);
+
+ // Path parameters
+ // If you want (for example) to return a specific instance of an object type by its ID
+ // you can use URLs like /led/1, led/2, ... - And you do not need to register one Resource
+ // Node per ID, but you can use wildcards in the route definition. The following route
+ // has two wildcards, and will match for example to /urlparam/foo/bar, where "foo" and "bar"
+ // are accessible parameters in the handler function.
+ // Note: The wildcards can only be used between slashes at the moment (so /urlparam* would
+ // not work).
+ ResourceNode * nodeURLParam = new ResourceNode("/urlparam/*/*", "GET", &handlePathParam);
+
+ // Add the root node to the server
+ secureServer.registerNode(nodeRoot);
+
+ // Add the SVG image
+ secureServer.registerNode(nodeSVG);
+
+ // Query parameter demo
+ secureServer.registerNode(nodeQueryDemo);
+
+ // Add the path parameter
+ // Note: The order of nodes may become important here. If you have one node for "/led" (e.g. list of LEDs)
+ // and one node for /led/* (LED details), you should register the non-parameterized version first. The server
+ // follows a first-match policy. If you would register the details node first, a call to /led/ will be targetted
+ // at the details handler function with an empty parameter, which is probably not what you want.
+ secureServer.registerNode(nodeURLParam);
+
+ // Add the 404 not found node to the server.
+ // The path is ignored for the default node.
+ secureServer.setDefaultNode(node404);
+
+ Serial.println("Starting server...");
+ secureServer.start();
+
+ if (secureServer.isRunning())
+ {
+ Serial.println("Server ready.");
+ }
+}
+
+void loop()
+{
+ // This call will let the server do its work
+ secureServer.loop();
+
+ // Other code would go here...
+ delay(1);
+}
diff --git a/examples/WT32_ETH01/Parameters/cert.h b/examples/WT32_ETH01/Parameters/cert.h
new file mode 100644
index 0000000..46c87e4
--- /dev/null
+++ b/examples/WT32_ETH01/Parameters/cert.h
@@ -0,0 +1,4 @@
+#ifndef CERT_H_
+#define CERT_H_
+ #error You have to run the srcipt extras/create_cert.sh to recreate these files
+#endif
diff --git a/examples/WT32_ETH01/Parameters/private_key.h b/examples/WT32_ETH01/Parameters/private_key.h
new file mode 100644
index 0000000..1d36c18
--- /dev/null
+++ b/examples/WT32_ETH01/Parameters/private_key.h
@@ -0,0 +1,4 @@
+#ifndef PRIVATE_KEY_H_
+#define PRIVATE_KEY_H_
+ #error You have to run the srcipt extras/create_cert.sh to recreate these files
+#endif
diff --git a/examples/WT32_ETH01/Put-Post-Echo/Put-Post-Echo.ino b/examples/WT32_ETH01/Put-Post-Echo/Put-Post-Echo.ino
new file mode 100644
index 0000000..2fef51f
--- /dev/null
+++ b/examples/WT32_ETH01/Put-Post-Echo/Put-Post-Echo.ino
@@ -0,0 +1,212 @@
+/**
+ Example for the WT32_ETH01 HTTP(S) Webserver
+
+ IMPORTANT NOTE:
+ To run this script, your need to
+ 1) Make sure to have certificate data available. You will find a
+ shell script (create_cert.sh) and instructions to do so in the library folder
+ under extras/
+
+ This script will install an HTTPS Server on your WT32_ETH01 with the following
+ functionalities:
+ - Show simple page on web server root
+ - Provide an Echo-Service for PUT/POST requests on /
+ - 404 for everything else
+*/
+
+// Include certificate data (see note above)
+#include "cert.h"
+#include "private_key.h"
+
+//////////////////////////////////////////////////
+
+// For WT32_ETH01
+#define DEBUG_ETHERNET_WEBSERVER_PORT Serial
+
+// Debug Level from 0 to 4
+#define _ETHERNET_WEBSERVER_LOGLEVEL_ 3
+
+#include
+
+// Select the IP address according to your local network
+IPAddress myIP(192, 168, 2, 232);
+IPAddress myGW(192, 168, 2, 1);
+IPAddress mySN(255, 255, 255, 0);
+
+// Google DNS Server IP
+IPAddress myDNS(8, 8, 8, 8);
+
+//////////////////////////////////////////////////
+
+// Includes for the server
+#include
+#include
+#include
+#include
+
+// The HTTPS Server comes in a separate namespace. For easier use, include it here.
+using namespace httpsserver;
+
+// Create an SSL certificate object from the files included above
+SSLCert cert = SSLCert(
+ example_crt_DER, example_crt_DER_len,
+ example_key_DER, example_key_DER_len
+ );
+
+// Create an SSL-enabled server that uses the certificate
+// The contstructor takes some more parameters, but we go for default values here.
+HTTPSServer secureServer = HTTPSServer(&cert);
+
+// Handler functions for the various URLs on the server
+// The signature is always the same for those functions. They get two parameters,
+// which are pointers to the request data (read request body, headers, ...) and
+// to the response data (write response, set status code, ...)
+
+void handleRoot(HTTPRequest * req, HTTPResponse * res)
+{
+ // Status code is 200 OK by default.
+ // We want to deliver a simple HTML page, so we send a corresponding content type:
+ res->setHeader("Content-Type", "text/html");
+
+ // The response implements the Print interface, so you can use it just like
+ // you would write to Serial etc.
+ res->println("");
+ res->println("");
+ res->println("Hello World!");
+ res->println("");
+ res->println("
Hello World!
");
+ res->print("
Your server is running for ");
+ // A bit of dynamic data: Show the uptime
+ res->print((int)(millis() / 1000), DEC);
+ res->println(" seconds.
");
+ res->print("
The easiest way to test the echo function is to use an HTTP debugging "
+ " tool where you are able to set a body and then let it call PUT or POST on"
+ " https://");
+ res->print(WiFi.localIP());
+ res->print("/. Otherwise you can use command line tools like curl:
"
+ "
cat <<EOF | curl --insecure -X PUT -d @- https://");
+ res->print(WiFi.localIP());
+ res->print("/\nHere goes your request \nbody\n"
+ "EOF
");
+ res->println("");
+ res->println("");
+}
+
+void handleEcho(HTTPRequest * req, HTTPResponse * res)
+{
+ // The echo callback will return the request body as response body.
+
+ // We use text/plain for the response
+ res->setHeader("Content-Type", "text/plain");
+
+ // Stream the incoming request body to the response body
+ // Theoretically, this should work for every request size.
+ byte buffer[256];
+
+ // HTTPRequest::requestComplete can be used to check whether the
+ // body has been parsed completely.
+ while (!(req->requestComplete()))
+ {
+ // HTTPRequest::readBytes provides access to the request body.
+ // It requires a buffer, the max buffer length and it will return
+ // the amount of bytes that have been written to the buffer.
+ size_t s = req->readBytes(buffer, 256);
+
+ // The response does not only implement the Print interface to
+ // write character data to the response but also the write function
+ // to write binary data to the response.
+ res->write(buffer, s);
+ }
+}
+
+void handle404(HTTPRequest * req, HTTPResponse * res)
+{
+ // Discard request body, if we received any
+ // We do this, as this is the default node and may also server POST/PUT requests
+ req->discardRequestBody();
+
+ // Set the response status
+ res->setStatusCode(404);
+ res->setStatusText("Not Found");
+
+ // Set content type of the response
+ res->setHeader("Content-Type", "text/html");
+
+ // Write a tiny HTTP page
+ res->println("");
+ res->println("");
+ res->println("Not Found");
+ res->println("
404 Not Found
The requested resource was not found on this server.
");
+ res->println("");
+}
+
+void setup()
+{
+ // For logging
+ Serial.begin(115200);
+ while (!Serial && millis() < 5000);
+
+ ///////////////////////////////////////////////
+
+ Serial.print("\nStarting Put-Post-Echo on " + String(ARDUINO_BOARD));
+ Serial.println(" with " + String(SHIELD_TYPE));
+ Serial.println(WEBSERVER_WT32_ETH01_VERSION);
+
+ // To be called before ETH.begin()
+ WT32_ETH01_onEvent();
+
+ //bool begin(uint8_t phy_addr=ETH_PHY_ADDR, int power=ETH_PHY_POWER, int mdc=ETH_PHY_MDC, int mdio=ETH_PHY_MDIO,
+ // eth_phy_type_t type=ETH_PHY_TYPE, eth_clock_mode_t clk_mode=ETH_CLK_MODE);
+ //ETH.begin(ETH_PHY_ADDR, ETH_PHY_POWER, ETH_PHY_MDC, ETH_PHY_MDIO, ETH_PHY_TYPE, ETH_CLK_MODE);
+ ETH.begin(ETH_PHY_ADDR, ETH_PHY_POWER);
+
+ // Static IP, leave without this line to get IP via DHCP
+ //bool config(IPAddress local_ip, IPAddress gateway, IPAddress subnet, IPAddress dns1 = 0, IPAddress dns2 = 0);
+ ETH.config(myIP, myGW, mySN, myDNS);
+
+ WT32_ETH01_waitForConnect();
+
+ Serial.print(F("HTTP EthernetWebServer is @ IP : "));
+ Serial.println(ETH.localIP());
+
+ ///////////////////////////////////////////////
+
+ // For every resource available on the server, we need to create a ResourceNode
+ // The ResourceNode links URL and HTTP method to a handler function
+ ResourceNode * nodeRoot = new ResourceNode("/", "GET", &handleRoot);
+ ResourceNode * node404 = new ResourceNode("", "GET", &handle404);
+
+ // Register the echo handler. You can use the same handler function for multiple
+ // nodes. Note also that we now have three resource nodes for the / URL, so
+ // the server uses only the method to distinguish between them.
+ ResourceNode * nodeEchoPut = new ResourceNode("/", "PUT", &handleEcho);
+ ResourceNode * nodeEchoPost = new ResourceNode("/", "POST", &handleEcho);
+
+ // Add the root node to the server
+ secureServer.registerNode(nodeRoot);
+
+ // Add the nodes for the echo service
+ secureServer.registerNode(nodeEchoPut);
+ secureServer.registerNode(nodeEchoPost);
+
+ // Add the 404 not found node to the server.
+ // The path is ignored for the default node.
+ secureServer.setDefaultNode(node404);
+
+ Serial.println("Starting server...");
+ secureServer.start();
+
+ if (secureServer.isRunning())
+ {
+ Serial.println("Server ready.");
+ }
+}
+
+void loop()
+{
+ // This call will let the server do its work
+ secureServer.loop();
+
+ // Other code would go here...
+ delay(1);
+}
diff --git a/examples/WT32_ETH01/Put-Post-Echo/cert.h b/examples/WT32_ETH01/Put-Post-Echo/cert.h
new file mode 100644
index 0000000..46c87e4
--- /dev/null
+++ b/examples/WT32_ETH01/Put-Post-Echo/cert.h
@@ -0,0 +1,4 @@
+#ifndef CERT_H_
+#define CERT_H_
+ #error You have to run the srcipt extras/create_cert.sh to recreate these files
+#endif
diff --git a/examples/WT32_ETH01/Put-Post-Echo/private_key.h b/examples/WT32_ETH01/Put-Post-Echo/private_key.h
new file mode 100644
index 0000000..1d36c18
--- /dev/null
+++ b/examples/WT32_ETH01/Put-Post-Echo/private_key.h
@@ -0,0 +1,4 @@
+#ifndef PRIVATE_KEY_H_
+#define PRIVATE_KEY_H_
+ #error You have to run the srcipt extras/create_cert.sh to recreate these files
+#endif
diff --git a/examples/WT32_ETH01/REST-API/REST-API.ino b/examples/WT32_ETH01/REST-API/REST-API.ino
new file mode 100644
index 0000000..8e7b667
--- /dev/null
+++ b/examples/WT32_ETH01/REST-API/REST-API.ino
@@ -0,0 +1,641 @@
+/**
+ Example for the WT32_ETH01 HTTP(S) Webserver
+
+ IMPORTANT NOTE:
+ This example is a bit more complex than the other ones, so be careful to
+ follow all steps.
+
+ Make sure to check out the more basic examples like Static-Page to understand
+ the fundamental principles of the API before proceeding with this sketch.
+
+ To run this script, you need to
+ 1) Install the SPIFFS File uploader into your Arduino IDE to be able to
+ upload static data to the webserver.
+ Follow the instructions at:
+ https://github.com/me-no-dev/arduino-esp32fs-plugin
+ 2) Upload the static files from the data/ directory of the example to your
+ module's SPIFFs by using "ESP32 Sketch Data Upload" from the tools menu.
+ If you face any problems, read the description of the libraray mentioned
+ above.
+ Note: If mounting SPIFFS fails, the script will wait for a serial connection
+ (open your serial monitor!) and ask if it should format the SPIFFS partition.
+ You may need this before uploading the data
+ Note: Make sure to select a partition layout that allows for SPIFFS in the
+ boards menu
+ 4) Have the ArduinoJSON library installed and available. (Tested with Version 5.13.4)
+ You'll find it at:
+ https://arduinojson.org/
+
+ This script will install an HTTPS Server on your WT32_ETH01 with the following
+ functionalities:
+ - Serve static files from the SPIFFS's data/public directory
+ - Provide a REST API at /api to receive the asynchronous http requests
+ - /api/uptime provides access to the current system uptime
+ - /api/events allows to register or delete events to turn PINs on/off
+ at certain times.
+ - Use Arduino JSON for body parsing and generation of responses.
+ - The certificate is generated on first run and stored to the SPIFFS in
+ the cert directory (so that the client cannot retrieve the private key)
+*/
+
+//////////////////////////////////////////////////
+
+// For WT32_ETH01
+#define DEBUG_ETHERNET_WEBSERVER_PORT Serial
+
+// Debug Level from 0 to 4
+#define _ETHERNET_WEBSERVER_LOGLEVEL_ 3
+
+#include
+
+// Select the IP address according to your local network
+IPAddress myIP(192, 168, 2, 232);
+IPAddress myGW(192, 168, 2, 1);
+IPAddress mySN(255, 255, 255, 0);
+
+// Google DNS Server IP
+IPAddress myDNS(8, 8, 8, 8);
+
+//////////////////////////////////////////////////
+
+// We will use SPIFFS and FS
+#include
+#include
+
+// We use JSON as data format. Make sure to have the lib available
+#include
+
+// Working with c++ strings
+#include
+
+// Define the name of the directory for public files in the SPIFFS parition
+#define DIR_PUBLIC "/public"
+
+// We need to specify some content-type mapping, so the resources get delivered with the
+// right content type and are displayed correctly in the browser
+char contentTypes[][2][32] =
+{
+ {".html", "text/html"},
+ {".css", "text/css"},
+ {".js", "application/javascript"},
+ {".json", "application/json"},
+ {".png", "image/png"},
+ {".jpg", "image/jpg"},
+ {"", ""}
+};
+
+// Includes for the server
+#include
+#include
+#include
+#include
+#include
+
+// The HTTPS Server comes in a separate namespace. For easier use, include it here.
+using namespace httpsserver;
+
+SSLCert * getCertificate();
+
+// We use the following struct to store GPIO events:
+#define MAX_EVENTS 20
+struct
+{
+ // is this event used (events that have been run will be set to false)
+ bool active;
+ // when should it be run?
+ unsigned long time;
+ // which GPIO should be changed?
+ int gpio;
+ // and to which state?
+ int state;
+} events[MAX_EVENTS];
+
+// We just create a reference to the server here. We cannot call the constructor unless
+// we have initialized the SPIFFS and read or created the certificate
+HTTPSServer * secureServer;
+
+
+/**
+ This function will either read the certificate and private key from SPIFFS or
+ create a self-signed certificate and write it to SPIFFS for next boot
+*/
+SSLCert * getCertificate()
+{
+ // Try to open key and cert file to see if they exist
+ File keyFile = SPIFFS.open("/key.der");
+ File certFile = SPIFFS.open("/cert.der");
+
+ // If now, create them
+ if (!keyFile || !certFile || keyFile.size() == 0 || certFile.size() == 0)
+ {
+ Serial.println("No certificate found in SPIFFS, generating a new one for you.");
+ Serial.println("If you face a Guru Meditation, give the script another try (or two...).");
+ Serial.println("This may take up to a minute, so please stand by :)");
+
+ SSLCert * newCert = new SSLCert();
+
+ // The part after the CN= is the domain that this certificate will match, in this
+ // case, it's esp32.local.
+ // However, as the certificate is self-signed, your browser won't trust the server
+ // anyway.
+ int res = createSelfSignedCert(*newCert, KEYSIZE_1024, "CN=esp32.local,O=acme,C=DE");
+
+ if (res == 0)
+ {
+ // We now have a certificate. We store it on the SPIFFS to restore it on next boot.
+
+ bool failure = false;
+
+ // Private key
+ keyFile = SPIFFS.open("/key.der", FILE_WRITE);
+
+ if (!keyFile || !keyFile.write(newCert->getPKData(), newCert->getPKLength()))
+ {
+ Serial.println("Could not write /key.der");
+ failure = true;
+ }
+
+ if (keyFile)
+ keyFile.close();
+
+ // Certificate
+ certFile = SPIFFS.open("/cert.der", FILE_WRITE);
+
+ if (!certFile || !certFile.write(newCert->getCertData(), newCert->getCertLength()))
+ {
+ Serial.println("Could not write /cert.der");
+ failure = true;
+ }
+
+ if (certFile)
+ certFile.close();
+
+ if (failure)
+ {
+ Serial.println("Certificate could not be stored permanently, generating new certificate on reboot...");
+ }
+
+ return newCert;
+
+ }
+ else
+ {
+ // Certificate generation failed. Inform the user.
+ Serial.println("An error occured during certificate generation.");
+ Serial.print("Error code is 0x");
+ Serial.println(res, HEX);
+ Serial.println("You may have a look at SSLCert.h to find the reason for this error.");
+
+ return NULL;
+ }
+
+ }
+ else
+ {
+ Serial.println("Reading certificate from SPIFFS.");
+
+ // The files exist, so we can create a certificate based on them
+ size_t keySize = keyFile.size();
+ size_t certSize = certFile.size();
+
+ uint8_t * keyBuffer = new uint8_t[keySize];
+
+ if (keyBuffer == NULL)
+ {
+ Serial.println("Not enough memory to load privat key");
+ return NULL;
+ }
+
+ uint8_t * certBuffer = new uint8_t[certSize];
+
+ if (certBuffer == NULL)
+ {
+ delete[] keyBuffer;
+ Serial.println("Not enough memory to load certificate");
+
+ return NULL;
+ }
+
+ keyFile.read(keyBuffer, keySize);
+ certFile.read(certBuffer, certSize);
+
+ // Close the files
+ keyFile.close();
+ certFile.close();
+ Serial.printf("Read %u bytes of certificate and %u bytes of key from SPIFFS\n", certSize, keySize);
+ return new SSLCert(certBuffer, certSize, keyBuffer, keySize);
+ }
+}
+
+/**
+ This handler function will try to load the requested resource from SPIFFS's /public folder.
+
+ If the method is not GET, it will throw 405, if the file is not found, it will throw 404.
+*/
+void handleSPIFFS(HTTPRequest * req, HTTPResponse * res)
+{
+
+ // We only handle GET here
+ if (req->getMethod() == "GET")
+ {
+ // Redirect / to /index.html
+ std::string reqFile = req->getRequestString() == "/" ? "/index.html" : req->getRequestString();
+
+ // Try to open the file
+ std::string filename = std::string(DIR_PUBLIC) + reqFile;
+
+ // Check if the file exists
+ if (!SPIFFS.exists(filename.c_str()))
+ {
+ // Send "404 Not Found" as response, as the file doesn't seem to exist
+ res->setStatusCode(404);
+ res->setStatusText("Not found");
+ res->println("404 Not Found");
+
+ return;
+ }
+
+ File file = SPIFFS.open(filename.c_str());
+
+ // Set length
+ res->setHeader("Content-Length", httpsserver::intToString(file.size()));
+
+ // Content-Type is guessed using the definition of the contentTypes-table defined above
+ int cTypeIdx = 0;
+
+ do
+ {
+ if (reqFile.rfind(contentTypes[cTypeIdx][0]) != std::string::npos)
+ {
+ res->setHeader("Content-Type", contentTypes[cTypeIdx][1]);
+ break;
+ }
+
+ cTypeIdx += 1;
+ } while (strlen(contentTypes[cTypeIdx][0]) > 0);
+
+ // Read the file and write it to the response
+ uint8_t buffer[256];
+ size_t length = 0;
+
+ do
+ {
+ length = file.read(buffer, 256);
+ res->write(buffer, length);
+ } while (length > 0);
+
+ file.close();
+ }
+ else
+ {
+ // If there's any body, discard it
+ req->discardRequestBody();
+ // Send "405 Method not allowed" as response
+ res->setStatusCode(405);
+ res->setStatusText("Method not allowed");
+ res->println("405 Method not allowed");
+ }
+}
+
+/**
+ This function will return the uptime in seconds as JSON object:
+ {"uptime": 42}
+*/
+void handleGetUptime(HTTPRequest * req, HTTPResponse * res)
+{
+ // Create a buffer of size 1 (pretty simple, we have just one key here)
+ StaticJsonBuffer jsonBuffer;
+ // Create an object at the root
+ JsonObject& obj = jsonBuffer.createObject();
+
+ // Set the uptime key to the uptime in seconds
+ obj["uptime"] = millis() / 1000;
+ // Set the content type of the response
+ res->setHeader("Content-Type", "application/json");
+ // As HTTPResponse implements the Print interface, this works fine. Just remember
+ // to use *, as we only have a pointer to the HTTPResponse here:
+ obj.printTo(*res);
+}
+
+/**
+ This handler will return a JSON array of currently active events for GET /api/events
+*/
+void handleGetEvents(HTTPRequest * req, HTTPResponse * res)
+{
+ // We need to calculate the capacity of the json buffer
+ int activeEvents = 0;
+
+ for (int i = 0; i < MAX_EVENTS; i++)
+ {
+ if (events[i].active)
+ activeEvents++;
+ }
+
+ // For each active event, we need 1 array element with 4 objects
+ const size_t capacity = JSON_ARRAY_SIZE(activeEvents) + activeEvents * JSON_OBJECT_SIZE(4);
+
+ // DynamicJsonBuffer is created on the heap instead of the stack
+ DynamicJsonBuffer jsonBuffer(capacity);
+ JsonArray& arr = jsonBuffer.createArray();
+
+ for (int i = 0; i < MAX_EVENTS; i++)
+ {
+ if (events[i].active)
+ {
+ JsonObject& eventObj = arr.createNestedObject();
+ eventObj["gpio"] = events[i].gpio;
+ eventObj["state"] = events[i].state;
+ eventObj["time"] = events[i].time;
+ // Add the index to allow delete and post to identify the element
+ eventObj["id"] = i;
+ }
+ }
+
+ // Print to response
+ res->setHeader("Content-Type", "application/json");
+ arr.printTo(*res);
+}
+
+void handlePostEvent(HTTPRequest * req, HTTPResponse * res)
+{
+ // We expect an object with 4 elements and add some buffer
+ const size_t capacity = JSON_OBJECT_SIZE(4) + 180;
+ DynamicJsonBuffer jsonBuffer(capacity);
+
+ // Create buffer to read request
+ char * buffer = new char[capacity + 1];
+ memset(buffer, 0, capacity + 1);
+
+ // Try to read request into buffer
+ size_t idx = 0;
+
+ // while "not everything read" or "buffer is full"
+ while (!req->requestComplete() && idx < capacity)
+ {
+ idx += req->readChars(buffer + idx, capacity - idx);
+ }
+
+ // If the request is still not read completely, we cannot process it.
+ if (!req->requestComplete())
+ {
+ res->setStatusCode(413);
+ res->setStatusText("Request entity too large");
+ res->println("413 Request entity too large");
+
+ // Clean up
+ delete[] buffer;
+
+ return;
+ }
+
+ // Parse the object
+ JsonObject& reqObj = jsonBuffer.parseObject(buffer);
+
+ // Check input data types
+ bool dataValid = true;
+
+ if (!reqObj.is("time") || !reqObj.is("gpio") || !reqObj.is("state"))
+ {
+ dataValid = false;
+ }
+
+ // Check actual values
+ unsigned long eTime = 0;
+ int eGpio = 0;
+ int eState = LOW;
+
+ if (dataValid)
+ {
+ eTime = reqObj["time"];
+
+ if (eTime < millis() / 1000)
+ dataValid = false;
+
+ eGpio = reqObj["gpio"];
+
+ if (!(eGpio == 25 || eGpio == 26 || eGpio == 27 || eGpio == 32 || eGpio == 33))
+ dataValid = false;
+
+ eState = reqObj["state"];
+
+ if (eState != HIGH && eState != LOW)
+ dataValid = false;
+ }
+
+ // Clean up, we don't need the buffer any longer
+ delete[] buffer;
+
+ // If something failed: 400
+ if (!dataValid)
+ {
+ res->setStatusCode(400);
+ res->setStatusText("Bad Request");
+ res->println("400 Bad Request");
+
+ return;
+ }
+
+ // Try to find an inactive event in the list to write the data to
+ int eventID = -1;
+
+ for (int i = 0; i < MAX_EVENTS && eventID == -1; i++)
+ {
+ if (!events[i].active)
+ {
+ eventID = i;
+ events[i].gpio = eGpio;
+ events[i].time = eTime;
+ events[i].state = eState;
+ events[i].active = true;
+ }
+ }
+
+
+ // Check if we could store the event
+ if (eventID > -1)
+ {
+ // Create a buffer for the response
+ StaticJsonBuffer resBuffer;
+
+ // Create an object at the root
+ JsonObject& resObj = resBuffer.createObject();
+
+ // Set the uptime key to the uptime in seconds
+ resObj["gpio"] = events[eventID].gpio;
+ resObj["state"] = events[eventID].state;
+ resObj["time"] = events[eventID].time;
+ resObj["id"] = eventID;
+
+ // Write the response
+ res->setHeader("Content-Type", "application/json");
+ resObj.printTo(*res);
+
+ }
+ else
+ {
+ // We could not store the event, no free slot.
+ res->setStatusCode(507);
+ res->setStatusText("Insufficient storage");
+ res->println("507 Insufficient storage");
+ }
+}
+
+/**
+ This handler will delete an event (meaning: deactive the event)
+*/
+void handleDeleteEvent(HTTPRequest * req, HTTPResponse * res)
+{
+ // Access the parameter from the URL. See Parameters example for more details on this
+ ResourceParameters * params = req->getParams();
+ size_t eid = std::atoi(params->getPathParameter(0).c_str());
+
+ if (eid < MAX_EVENTS)
+ {
+ // Set the inactive flag
+ events[eid].active = false;
+ // And return a successful response without body
+ res->setStatusCode(204);
+ res->setStatusText("No Content");
+ }
+ else
+ {
+ // Send error message
+ res->setStatusCode(400);
+ res->setStatusText("Bad Request");
+ res->println("400 Bad Request");
+ }
+}
+
+void setup()
+{
+ // For logging
+ Serial.begin(115200);
+ while (!Serial && millis() < 5000);
+
+ ///////////////////////////////////////////////
+
+ Serial.print("\nStarting REST-API on " + String(ARDUINO_BOARD));
+ Serial.println(" with " + String(SHIELD_TYPE));
+ Serial.println(WEBSERVER_WT32_ETH01_VERSION);
+
+ // Set the pins that we will use as output pins
+ pinMode(25, OUTPUT);
+ pinMode(26, OUTPUT);
+ pinMode(27, OUTPUT);
+ pinMode(32, OUTPUT);
+ pinMode(33, OUTPUT);
+
+ // Try to mount SPIFFS without formatting on failure
+ if (!SPIFFS.begin(false))
+ {
+ // If SPIFFS does not work, we wait for serial connection...
+ while (!Serial);
+ delay(1000);
+
+ // Ask to format SPIFFS using serial interface
+ Serial.print("Mounting SPIFFS failed. Try formatting? (y/n): ");
+ while (!Serial.available());
+ Serial.println();
+
+ // If the user did not accept to try formatting SPIFFS or formatting failed:
+ if (Serial.read() != 'y' || !SPIFFS.begin(true)) {
+ Serial.println("SPIFFS not available. Stop.");
+ while (true);
+ }
+ Serial.println("SPIFFS has been formated.");
+ }
+ Serial.println("SPIFFS has been mounted.");
+
+ // Now that SPIFFS is ready, we can create or load the certificate
+ SSLCert *cert = getCertificate();
+ if (cert == NULL) {
+ Serial.println("Could not load certificate. Stop.");
+ while (true);
+ }
+
+ // Initialize event structure:
+ for (int i = 0; i < MAX_EVENTS; i++) {
+ events[i].active = false;
+ events[i].gpio = 0;
+ events[i].state = LOW;
+ events[i].time = 0;
+ }
+
+ ///////////////////////////////////////////////
+
+ // To be called before ETH.begin()
+ WT32_ETH01_onEvent();
+
+ //bool begin(uint8_t phy_addr=ETH_PHY_ADDR, int power=ETH_PHY_POWER, int mdc=ETH_PHY_MDC, int mdio=ETH_PHY_MDIO,
+ // eth_phy_type_t type=ETH_PHY_TYPE, eth_clock_mode_t clk_mode=ETH_CLK_MODE);
+ //ETH.begin(ETH_PHY_ADDR, ETH_PHY_POWER, ETH_PHY_MDC, ETH_PHY_MDIO, ETH_PHY_TYPE, ETH_CLK_MODE);
+ ETH.begin(ETH_PHY_ADDR, ETH_PHY_POWER);
+
+ // Static IP, leave without this line to get IP via DHCP
+ //bool config(IPAddress local_ip, IPAddress gateway, IPAddress subnet, IPAddress dns1 = 0, IPAddress dns2 = 0);
+ ETH.config(myIP, myGW, mySN, myDNS);
+
+ WT32_ETH01_waitForConnect();
+
+ Serial.print(F("HTTP EthernetWebServer is @ IP : "));
+ Serial.println(ETH.localIP());
+
+ ///////////////////////////////////////////////
+
+ // Create the server with the certificate we loaded before
+ secureServer = new HTTPSServer(cert);
+
+ // We register the SPIFFS handler as the default node, so every request that does
+ // not hit any other node will be redirected to the file system.
+ ResourceNode * spiffsNode = new ResourceNode("", "", &handleSPIFFS);
+ secureServer->setDefaultNode(spiffsNode);
+
+ // Add a handler that serves the current system uptime at GET /api/uptime
+ ResourceNode * uptimeNode = new ResourceNode("/api/uptime", "GET", &handleGetUptime);
+ secureServer->registerNode(uptimeNode);
+
+ // Add the handler nodes that deal with modifying the events:
+ ResourceNode * getEventsNode = new ResourceNode("/api/events", "GET", &handleGetEvents);
+ secureServer->registerNode(getEventsNode);
+ ResourceNode * postEventNode = new ResourceNode("/api/events", "POST", &handlePostEvent);
+ secureServer->registerNode(postEventNode);
+ ResourceNode * deleteEventNode = new ResourceNode("/api/events/*", "DELETE", &handleDeleteEvent);
+ secureServer->registerNode(deleteEventNode);
+
+ Serial.println("Starting server...");
+ secureServer->start();
+
+ if (secureServer->isRunning())
+ {
+ Serial.println("Server ready.");
+ }
+}
+
+void loop()
+{
+ // This call will let the server do its work
+ secureServer->loop();
+
+ // Here we handle the events
+ unsigned long now = millis() / 1000;
+
+ for (int i = 0; i < MAX_EVENTS; i++)
+ {
+ // Only handle active events:
+ if (events[i].active)
+ {
+ // Only if the counter has recently been exceeded
+ if (events[i].time < now)
+ {
+ // Apply the state change
+ digitalWrite(events[i].gpio, events[i].state);
+
+ // Deactivate the event so it doesn't fire again
+ events[i].active = false;
+ }
+ }
+ }
+
+ // Other code would go here...
+ delay(1);
+}
diff --git a/examples/WT32_ETH01/REST-API/cert.h b/examples/WT32_ETH01/REST-API/cert.h
new file mode 100644
index 0000000..46c87e4
--- /dev/null
+++ b/examples/WT32_ETH01/REST-API/cert.h
@@ -0,0 +1,4 @@
+#ifndef CERT_H_
+#define CERT_H_
+ #error You have to run the srcipt extras/create_cert.sh to recreate these files
+#endif
diff --git a/examples/WT32_ETH01/REST-API/data/public/index.html b/examples/WT32_ETH01/REST-API/data/public/index.html
new file mode 100644
index 0000000..be0a8bb
--- /dev/null
+++ b/examples/WT32_ETH01/REST-API/data/public/index.html
@@ -0,0 +1,230 @@
+
+
+
+ esp32_https_server - REST API example
+
+
+
+
+
+
esp32_https_server - REST API example
+
Current system time:loading...
+
+
Current Events
+
+
+
+
Time
+
GPIO
+
New State
+
+
+
+
+
+
+
Add a New Event
+
+
About This Example
+
This example shows you a web-page that can be used to control GPIOs 25, 26, 27, 32 and 33 to be turned on or off at a given time.
+
For simplicity, we use the system uptime here, which you can see at the top of the page.
+
The page uses the REST API provided at /api to commuicate with the ESP32.
+
GET /api/uptime returns the current system uptime:
+
{"uptime":42}
+
This node is polled every 10 seconds to stay synchronized.
+
GET /api/events returns the current list of events:
+
[{"gpio":27,"state":1,"time":42,"id":0}, ...]
+
This node is polled every 60 seconds to stay synchronized
+
POST /api/events can be used to add new events. The response object will contain an additional id that can be used to refer to the event.
+
DELETE /api/events/id will delete the event with the specified id. If events are executed (time < uptime), they are deleted as well.
+
The client side javascript synchronizes your view even if not explicit request is sent to the ESP32.
"],_default:[0,"",""]};ge.optgroup=ge.option,ge.tbody=ge.tfoot=ge.colgroup=ge.caption=ge.thead,ge.th=ge.td;function ye(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&N(e,t)?w.merge([e],n):n}function ve(e,t){for(var n=0,r=e.length;n-1)i&&i.push(o);else if(l=w.contains(o.ownerDocument,o),a=ye(f.appendChild(o),"script"),l&&ve(a),n){c=0;while(o=a[c++])he.test(o.type||"")&&n.push(o)}return f}!function(){var e=r.createDocumentFragment().appendChild(r.createElement("div")),t=r.createElement("input");t.setAttribute("type","radio"),t.setAttribute("checked","checked"),t.setAttribute("name","t"),e.appendChild(t),h.checkClone=e.cloneNode(!0).cloneNode(!0).lastChild.checked,e.innerHTML="",h.noCloneChecked=!!e.cloneNode(!0).lastChild.defaultValue}();var be=r.documentElement,we=/^key/,Te=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,Ce=/^([^.]*)(?:\.(.+)|)/;function Ee(){return!0}function ke(){return!1}function Se(){try{return r.activeElement}catch(e){}}function De(e,t,n,r,i,o){var a,s;if("object"==typeof t){"string"!=typeof n&&(r=r||n,n=void 0);for(s in t)De(e,s,n,r,t[s],o);return e}if(null==r&&null==i?(i=n,r=n=void 0):null==i&&("string"==typeof n?(i=r,r=void 0):(i=r,r=n,n=void 0)),!1===i)i=ke;else if(!i)return e;return 1===o&&(a=i,(i=function(e){return w().off(e),a.apply(this,arguments)}).guid=a.guid||(a.guid=w.guid++)),e.each(function(){w.event.add(this,t,i,r,n)})}w.event={global:{},add:function(e,t,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,y=J.get(e);if(y){n.handler&&(n=(o=n).handler,i=o.selector),i&&w.find.matchesSelector(be,i),n.guid||(n.guid=w.guid++),(u=y.events)||(u=y.events={}),(a=y.handle)||(a=y.handle=function(t){return"undefined"!=typeof w&&w.event.triggered!==t.type?w.event.dispatch.apply(e,arguments):void 0}),l=(t=(t||"").match(M)||[""]).length;while(l--)d=g=(s=Ce.exec(t[l])||[])[1],h=(s[2]||"").split(".").sort(),d&&(f=w.event.special[d]||{},d=(i?f.delegateType:f.bindType)||d,f=w.event.special[d]||{},c=w.extend({type:d,origType:g,data:r,handler:n,guid:n.guid,selector:i,needsContext:i&&w.expr.match.needsContext.test(i),namespace:h.join(".")},o),(p=u[d])||((p=u[d]=[]).delegateCount=0,f.setup&&!1!==f.setup.call(e,r,h,a)||e.addEventListener&&e.addEventListener(d,a)),f.add&&(f.add.call(e,c),c.handler.guid||(c.handler.guid=n.guid)),i?p.splice(p.delegateCount++,0,c):p.push(c),w.event.global[d]=!0)}},remove:function(e,t,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,y=J.hasData(e)&&J.get(e);if(y&&(u=y.events)){l=(t=(t||"").match(M)||[""]).length;while(l--)if(s=Ce.exec(t[l])||[],d=g=s[1],h=(s[2]||"").split(".").sort(),d){f=w.event.special[d]||{},p=u[d=(r?f.delegateType:f.bindType)||d]||[],s=s[2]&&new RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"),a=o=p.length;while(o--)c=p[o],!i&&g!==c.origType||n&&n.guid!==c.guid||s&&!s.test(c.namespace)||r&&r!==c.selector&&("**"!==r||!c.selector)||(p.splice(o,1),c.selector&&p.delegateCount--,f.remove&&f.remove.call(e,c));a&&!p.length&&(f.teardown&&!1!==f.teardown.call(e,h,y.handle)||w.removeEvent(e,d,y.handle),delete u[d])}else for(d in u)w.event.remove(e,d+t[l],n,r,!0);w.isEmptyObject(u)&&J.remove(e,"handle events")}},dispatch:function(e){var t=w.event.fix(e),n,r,i,o,a,s,u=new Array(arguments.length),l=(J.get(this,"events")||{})[t.type]||[],c=w.event.special[t.type]||{};for(u[0]=t,n=1;n=1))for(;l!==this;l=l.parentNode||this)if(1===l.nodeType&&("click"!==e.type||!0!==l.disabled)){for(o=[],a={},n=0;n-1:w.find(i,this,null,[l]).length),a[i]&&o.push(r);o.length&&s.push({elem:l,handlers:o})}return l=this,u\x20\t\r\n\f]*)[^>]*)\/>/gi,Ae=/\n"
+ "\n"
+ "\n"
+ );
+}
+
+void setup()
+{
+ // Initialize the slots
+ for (int i = 0; i < MAX_CLIENTS; i++)
+ activeClients[i] = nullptr;
+
+ // For logging
+ Serial.begin(115200);
+ while (!Serial && millis() < 5000);
+
+ ///////////////////////////////////////////////
+
+ Serial.print("\nStarting HTTP-and-HTTPS on " + String(ARDUINO_BOARD));
+ Serial.println(" with " + String(SHIELD_TYPE));
+ Serial.println(WEBSERVER_WT32_ETH01_VERSION);
+
+ // To be called before ETH.begin()
+ WT32_ETH01_onEvent();
+
+ //bool begin(uint8_t phy_addr=ETH_PHY_ADDR, int power=ETH_PHY_POWER, int mdc=ETH_PHY_MDC, int mdio=ETH_PHY_MDIO,
+ // eth_phy_type_t type=ETH_PHY_TYPE, eth_clock_mode_t clk_mode=ETH_CLK_MODE);
+ //ETH.begin(ETH_PHY_ADDR, ETH_PHY_POWER, ETH_PHY_MDC, ETH_PHY_MDIO, ETH_PHY_TYPE, ETH_CLK_MODE);
+ ETH.begin(ETH_PHY_ADDR, ETH_PHY_POWER);
+
+ // Static IP, leave without this line to get IP via DHCP
+ //bool config(IPAddress local_ip, IPAddress gateway, IPAddress subnet, IPAddress dns1 = 0, IPAddress dns2 = 0);
+ ETH.config(myIP, myGW, mySN, myDNS);
+
+ WT32_ETH01_waitForConnect();
+
+ Serial.print(F("HTTP EthernetWebServer is @ IP : "));
+ Serial.println(ETH.localIP());
+
+ ///////////////////////////////////////////////
+
+ // For every resource available on the server, we need to create a ResourceNode
+ // The ResourceNode links URL and HTTP method to a handler function
+ ResourceNode * nodeRoot = new ResourceNode("/", "GET", &handleRoot);
+ ResourceNode * node404 = new ResourceNode("", "GET", &handle404);
+
+ // Add the root node to the server
+ secureServer.registerNode(nodeRoot);
+
+ // The websocket handler can be linked to the server by using a WebsocketNode:
+ // (Note that the standard defines GET as the only allowed method here,
+ // so you do not need to pass it explicitly)
+ WebsocketNode * chatNode = new WebsocketNode("/chat", &ChatHandler::create);
+
+ // Adding the node to the server works in the same way as for all other nodes
+ secureServer.registerNode(chatNode);
+
+ // Finally, add the 404 not found node to the server.
+ // The path is ignored for the default node.
+ secureServer.setDefaultNode(node404);
+
+ Serial.println("Starting server...");
+ secureServer.start();
+
+ if (secureServer.isRunning())
+ {
+ Serial.print("Server ready. Open the following URL in multiple browser windows to start chatting: https://");
+ Serial.println(WiFi.localIP());
+ }
+}
+
+void loop()
+{
+ // This call will let the server do its work
+ secureServer.loop();
+
+ // Other code would go here...
+ delay(1);
+}
diff --git a/examples/WT32_ETH01/Websocket-Chat/cert.h b/examples/WT32_ETH01/Websocket-Chat/cert.h
new file mode 100644
index 0000000..46c87e4
--- /dev/null
+++ b/examples/WT32_ETH01/Websocket-Chat/cert.h
@@ -0,0 +1,4 @@
+#ifndef CERT_H_
+#define CERT_H_
+ #error You have to run the srcipt extras/create_cert.sh to recreate these files
+#endif
diff --git a/examples/WT32_ETH01/Websocket-Chat/private_key.h b/examples/WT32_ETH01/Websocket-Chat/private_key.h
new file mode 100644
index 0000000..1d36c18
--- /dev/null
+++ b/examples/WT32_ETH01/Websocket-Chat/private_key.h
@@ -0,0 +1,4 @@
+#ifndef PRIVATE_KEY_H_
+#define PRIVATE_KEY_H_
+ #error You have to run the srcipt extras/create_cert.sh to recreate these files
+#endif
From b0ae5e96dd6ac33f5a0238ffcfec98f54eaf6a31 Mon Sep 17 00:00:00 2001
From: Khoi Hoang <57012152+khoih-prog@users.noreply.github.com>
Date: Mon, 26 Sep 2022 22:10:40 -0400
Subject: [PATCH 3/5] v1.1.0 to add support to WT32_ETH01
---
CHANGELOG.md | 12 ++++++++++++
README.md | 11 ++++-------
library.json | 4 ++--
library.properties | 11 ++++++-----
4 files changed, 24 insertions(+), 14 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index f9ba97b..b75efe6 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -14,6 +14,18 @@ Breaking changes:
–
+## [v1.1.0](https://github.com/fhessel/esp32_https_server/releases/tag/v1.0.0)
+
+New functionality:
+
+* Add examples to support WT32_ETH01 using LAN8720
+
+Bug fixes:
+
+* Fix compile error for using `hwcrypto/sha.h`
+
+=========================================================
+
## [v1.0.0](https://github.com/fhessel/esp32_https_server/releases/tag/v1.0.0)
New functionality:
diff --git a/README.md b/README.md
index 33773dd..88d2f04 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,5 @@
# ESP32 HTTPS Server
- 
-
This repository contains an HTTPS server library that can be used with the [ESP32 Arduino Core](https://github.com/espressif/arduino-esp32). It supports HTTP as well.
## Features
@@ -73,19 +71,18 @@ git clone https://github.com/fhessel/esp32_https_server.git
> **Note:** To run the examples (except for the _Self-Signed-Certificates_ example), you need to execute the script extras/create_cert.sh first (see [Issue #26](https://github.com/fhessel/esp32_https_server/issues/26) for Windows). This script will create a simple CA to sign certificates that are used with the examples. Some notes on the usage can be found in the extras/README.md file.
-You will find several examples showing how you can use the library (roughly ordered by complexity):
+You will find several examples showing how you can use the library:
- [Static-Page](examples/Static-Page/Static-Page.ino): Short example showing how to serve some static resources with the server. You should start with this sketch and get familiar with it before having a look at the more complex examples.
- [Parameters](examples/Parameters/Parameters.ino): Shows how you can access request parameters (the part after the question mark in the URL) or parameters in dynamic URLs (like /led/1, /led/2, ...)
-- [Parameter-Validation](examples/Parameter-Validation/Parameter-Validation.ino): Shows how you can integrate validator functions to do formal checks on parameters in your URL.
- [Put-Post-Echo](examples/Put-Post-Echo/Put-Post-Echo.ino): Implements a simple echo service for PUT and POST requests that returns the request body as response body. Also shows how to differentiate between multiple HTTP methods for the same URL.
- [HTTPS-and-HTTP](examples/HTTPS-and-HTTP/HTTPS-and-HTTP.ino): Shows how to serve resources via HTTP and HTTPS in parallel and how to check if the user is using a secure connection during request handling
-- [HTML-Forms](examples/HTML-Forms/HTML-Forms.ino): Shows how to use body parsers to handle requests created from HTML forms (access text field contents, handle file upload, etc.).
-- [Async-Server](examples/Async-Server/Async-Server.ino): Like the Static-Page example, but the server runs in a separate task on the ESP32, so you do not need to call the loop() function in your main sketch.
-- [Self-Signed-Certificate](examples/Self-Signed-Certificate/Self-Signed-Certificate.ino): Shows how to generate a self-signed certificate on the fly on the ESP when the sketch starts. You do not need to run `create_cert.sh` to use this example.
- [Middleware](examples/Middleware/Middleware.ino): Shows how to use the middleware API for logging. Middleware functions are defined very similar to webservers like Express.
- [Authentication](examples/Authentication/Authentication.ino): Implements a chain of two middleware functions to handle authentication and authorization using HTTP Basic Auth.
+- [Async-Server](examples/Async-Server/Async-Server.ino): Like the Static-Page example, but the server runs in a separate task on the ESP32, so you do not need to call the loop() function in your main sketch.
- [Websocket-Chat](examples/Websocket-Chat/Websocket-Chat.ino): Provides a browser-based chat built on top of websockets. **Note:** Websockets are still under development!
+- [Parameter-Validation](examples/Parameter-Validation/Parameter-Validation.ino): Shows how you can integrate validator functions to do formal checks on parameters in your URL.
+- [Self-Signed-Certificate](examples/Self-Signed-Certificate/Self-Signed-Certificate.ino): Shows how to generate a self-signed certificate on the fly on the ESP when the sketch starts. You do not need to run `create_cert.sh` to use this example.
- [REST-API](examples/REST-API/REST-API.ino): Uses [ArduinoJSON](https://arduinojson.org/) and [SPIFFS file upload](https://github.com/me-no-dev/arduino-esp32fs-plugin) to serve a small web interface that provides a REST API.
If you encounter error messages that cert.h or private\_key.h are missing when running an example, make sure to run create\_cert.sh first (see Setup Instructions).
diff --git a/library.json b/library.json
index 0456c47..9b45e5d 100644
--- a/library.json
+++ b/library.json
@@ -1,6 +1,6 @@
{
"name": "esp32_https_server",
- "keywords": "communication, esp32, http, https, server, ssl, tls, webserver, websockets",
+ "keywords": "communication, esp32, http, https, server, ssl, tls, webserver, websockets, wt32-eth01",
"description": "Alternative ESP32 Webserver implementation for the ESP32, supporting HTTPS and HTTP. The library provides TLS support and simultaneous connections. It can be used to run an HTTP or HTTPS server, or both in parallel. The server's resources are defined through handler and middleware functions, giving an easy start to everyone who has worked with frameworks like Express or Servlets before.",
"repository":
{
@@ -8,7 +8,7 @@
"url": "https://github.com/fhessel/esp32_https_server.git"
},
"license": "MIT",
- "version": "1.0.0",
+ "version": "1.1.0",
"frameworks": "arduino",
"platforms": ["espressif32"]
}
diff --git a/library.properties b/library.properties
index 3ffb202..e603bee 100644
--- a/library.properties
+++ b/library.properties
@@ -1,10 +1,11 @@
name=ESP32_HTTPS_Server
-version=1.0.0
-author=Frank Hessel
-maintainer=Frank Hessel
-sentence=Alternative ESP32 Webserver implementation for the ESP32, supporting HTTPS and HTTP.
+version=1.1.0
+author=Frank Hessel ,Khoi Hoang
+maintainer=Khoi Hoang
+sentence=Alternative ESP32 Webserver implementation for the ESP32 and WT32-ETH01, supporting HTTPS and HTTP.
paragraph=The library provides TLS support and simultaneous connections. It can be used to run an HTTP or HTTPS server, or both in parallel. The server's resources are defined through handler and middleware functions, giving an easy start to everyone who has worked with frameworks like Express or Servlets before.
category=Communication
-url=https://github.com/fhessel/esp32_https_server
+url=https://github.com/khoih-prog/esp32_https_server
architectures=esp32
+depends=WebServer_WT32_ETH01
includes=HTTPSServer.hpp,HTTPRequest.hpp,HTTPResponse.hpp
From c3dcebdbaa5651477050727ae3fe51d7060541b8 Mon Sep 17 00:00:00 2001
From: Khoi Hoang <57012152+khoih-prog@users.noreply.github.com>
Date: Tue, 27 Sep 2022 13:36:12 -0400
Subject: [PATCH 4/5] Update examples
---
examples/Async-Server/Async-Server.ino | 146 ++---
examples/Async-Server/cert.h | 4 +
examples/Async-Server/private_key.h | 4 +
examples/Authentication/Authentication.ino | 283 +++++-----
examples/Authentication/cert.h | 4 +
examples/Authentication/private_key.h | 4 +
examples/HTML-Forms/HTML-Forms.ino | 471 ++++++++-------
examples/HTML-Forms/cert.h | 4 +
examples/HTML-Forms/private_key.h | 4 +
examples/HTTPS-and-HTTP/HTTPS-and-HTTP.ino | 153 ++---
examples/HTTPS-and-HTTP/cert.h | 4 +
examples/HTTPS-and-HTTP/private_key.h | 4 +
examples/Middleware/Middleware.ino | 157 ++---
examples/Middleware/cert.h | 4 +
examples/Middleware/private_key.h | 4 +
.../Parameter-Validation.ino | 299 +++++-----
examples/Parameter-Validation/cert.h | 4 +
examples/Parameter-Validation/private_key.h | 4 +
examples/Parameters/Parameters.ino | 279 +++++----
examples/Parameters/cert.h | 4 +
examples/Parameters/private_key.h | 4 +
examples/Put-Post-Echo/Put-Post-Echo.ino | 186 +++---
examples/Put-Post-Echo/cert.h | 4 +
examples/Put-Post-Echo/private_key.h | 4 +
examples/REST-API/REST-API.ino | 534 +++++++++++-------
examples/REST-API/cert.h | 4 +
examples/REST-API/private_key.h | 4 +
.../Self-Signed-Certificate.ino | 157 ++---
examples/Self-Signed-Certificate/cert.h | 4 +
.../Self-Signed-Certificate/private_key.h | 4 +
examples/Static-Page/Static-Page.ino | 156 ++---
examples/Static-Page/cert.h | 4 +
examples/Static-Page/private_key.h | 4 +
.../WT32_ETH01/Async-Server/Async-Server.ino | 2 +-
examples/WT32_ETH01/Async-Server/cert.h | 2 +-
.../WT32_ETH01/Async-Server/private_key.h | 2 +-
.../Authentication/Authentication.ino | 4 +-
examples/WT32_ETH01/Authentication/cert.h | 2 +-
.../WT32_ETH01/Authentication/private_key.h | 2 +-
examples/WT32_ETH01/HTML-Forms/HTML-Forms.ino | 2 +-
examples/WT32_ETH01/HTML-Forms/cert.h | 2 +-
examples/WT32_ETH01/HTML-Forms/private_key.h | 2 +-
.../HTTPS-and-HTTP/HTTPS-and-HTTP.ino | 2 +-
examples/WT32_ETH01/HTTPS-and-HTTP/cert.h | 2 +-
.../WT32_ETH01/HTTPS-and-HTTP/private_key.h | 2 +-
examples/WT32_ETH01/Middleware/Middleware.ino | 2 +-
examples/WT32_ETH01/Middleware/cert.h | 2 +-
examples/WT32_ETH01/Middleware/private_key.h | 2 +-
.../Parameter-Validation.ino | 2 +-
.../WT32_ETH01/Parameter-Validation/cert.h | 2 +-
.../Parameter-Validation/private_key.h | 2 +-
examples/WT32_ETH01/Parameters/Parameters.ino | 2 +-
examples/WT32_ETH01/Parameters/cert.h | 2 +-
examples/WT32_ETH01/Parameters/private_key.h | 2 +-
.../Put-Post-Echo/Put-Post-Echo.ino | 2 +-
examples/WT32_ETH01/Put-Post-Echo/cert.h | 2 +-
.../WT32_ETH01/Put-Post-Echo/private_key.h | 2 +-
examples/WT32_ETH01/REST-API/REST-API.ino | 15 +-
examples/WT32_ETH01/REST-API/cert.h | 2 +-
examples/WT32_ETH01/REST-API/private_key.h | 2 +-
.../Self-Signed-Certificate.ino | 2 +-
.../WT32_ETH01/Self-Signed-Certificate/cert.h | 2 +-
.../Self-Signed-Certificate/private_key.h | 2 +-
.../WT32_ETH01/Static-Page/Static-Page.ino | 3 +-
examples/WT32_ETH01/Static-Page/cert.h | 2 +-
examples/WT32_ETH01/Static-Page/private_key.h | 2 +-
.../Websocket-Chat/Websocket-Chat.ino | 5 +-
examples/WT32_ETH01/Websocket-Chat/cert.h | 2 +-
.../WT32_ETH01/Websocket-Chat/private_key.h | 2 +-
examples/Websocket-Chat/Websocket-Chat.ino | 229 ++++----
examples/Websocket-Chat/cert.h | 4 +
examples/Websocket-Chat/private_key.h | 4 +
72 files changed, 1793 insertions(+), 1444 deletions(-)
create mode 100644 examples/Async-Server/cert.h
create mode 100644 examples/Async-Server/private_key.h
create mode 100644 examples/Authentication/cert.h
create mode 100644 examples/Authentication/private_key.h
create mode 100644 examples/HTML-Forms/cert.h
create mode 100644 examples/HTML-Forms/private_key.h
create mode 100644 examples/HTTPS-and-HTTP/cert.h
create mode 100644 examples/HTTPS-and-HTTP/private_key.h
create mode 100644 examples/Middleware/cert.h
create mode 100644 examples/Middleware/private_key.h
create mode 100644 examples/Parameter-Validation/cert.h
create mode 100644 examples/Parameter-Validation/private_key.h
create mode 100644 examples/Parameters/cert.h
create mode 100644 examples/Parameters/private_key.h
create mode 100644 examples/Put-Post-Echo/cert.h
create mode 100644 examples/Put-Post-Echo/private_key.h
create mode 100644 examples/REST-API/cert.h
create mode 100644 examples/REST-API/private_key.h
create mode 100644 examples/Self-Signed-Certificate/cert.h
create mode 100644 examples/Self-Signed-Certificate/private_key.h
create mode 100644 examples/Static-Page/cert.h
create mode 100644 examples/Static-Page/private_key.h
create mode 100644 examples/Websocket-Chat/cert.h
create mode 100644 examples/Websocket-Chat/private_key.h
diff --git a/examples/Async-Server/Async-Server.ino b/examples/Async-Server/Async-Server.ino
index d987a51..8fed51d 100644
--- a/examples/Async-Server/Async-Server.ino
+++ b/examples/Async-Server/Async-Server.ino
@@ -1,31 +1,31 @@
/**
- * Example for the ESP32 HTTP(S) Webserver
- *
- * IMPORTANT NOTE:
- * To run this script, your need to
- * 1) Enter your WiFi SSID and PSK below this comment
- * 2) Make sure to have certificate data available. You will find a
- * shell script and instructions to do so in the library folder
- * under extras/
- *
- * This script will install an HTTPS Server on your ESP32 with the following
- * functionalities:
- * - Show simple page on web server root
- * - 404 for everything else
- * The server will be run in a separate task, so that you can do your own stuff
- * in the loop() function.
- * Everything else is just like the Static-Page example
- */
+ Example for the ESP32 HTTP(S) Webserver
+
+ IMPORTANT NOTE:
+ To run this script, your need to
+ 1) Enter your WiFi SSID and PSK below this comment
+ 2) Make sure to have certificate data available. You will find a
+ shell script and instructions to do so in the library folder
+ under extras/
+
+ This script will install an HTTPS Server on your ESP32 with the following
+ functionalities:
+ - Show simple page on web server root
+ - 404 for everything else
+ The server will be run in a separate task, so that you can do your own stuff
+ in the loop() function.
+ Everything else is just like the Static-Page example
+*/
// TODO: Configure your WiFi here
-#define WIFI_SSID ""
-#define WIFI_PSK ""
+#define WIFI_SSID "your_ssid"
+#define WIFI_PSK "12345678"
/** Check if we have multiple cores */
#if CONFIG_FREERTOS_UNICORE
-#define ARDUINO_RUNNING_CORE 0
+ #define ARDUINO_RUNNING_CORE 0
#else
-#define ARDUINO_RUNNING_CORE 1
+ #define ARDUINO_RUNNING_CORE 1
#endif
// Include certificate data (see note above)
@@ -46,53 +46,15 @@ using namespace httpsserver;
// Create an SSL certificate object from the files included above
SSLCert cert = SSLCert(
- example_crt_DER, example_crt_DER_len,
- example_key_DER, example_key_DER_len
-);
+ example_crt_DER, example_crt_DER_len,
+ example_key_DER, example_key_DER_len
+ );
// Create an SSL-enabled server that uses the certificate
HTTPSServer secureServer = HTTPSServer(&cert);
-// Declare some handler functions for the various URLs on the server
-void handleRoot(HTTPRequest * req, HTTPResponse * res);
-void handle404(HTTPRequest * req, HTTPResponse * res);
-
-// We declare a function that will be the entry-point for the task that is going to be
-// created.
-void serverTask(void *params);
-
-void setup() {
- // For logging
- Serial.begin(115200);
-
- // Connect to WiFi
- Serial.println("Setting up WiFi");
- WiFi.begin(WIFI_SSID, WIFI_PSK);
- while (WiFi.status() != WL_CONNECTED) {
- Serial.print(".");
- delay(500);
- }
- Serial.print("Connected. IP=");
- Serial.println(WiFi.localIP());
-
- // Setup the server as a separate task.
- Serial.println("Creating server task... ");
- // We pass:
- // serverTask - the function that should be run as separate task
- // "https443" - a name for the task (mainly used for logging)
- // 6144 - stack size in byte. If you want up to four clients, you should
- // not go below 6kB. If your stack is too small, you will encounter
- // Panic and stack canary exceptions, usually during the call to
- // SSL_accept.
- xTaskCreatePinnedToCore(serverTask, "https443", 6144, NULL, 1, NULL, ARDUINO_RUNNING_CORE);
-}
-
-void loop() {
- Serial.println("loop()");
- delay(5000);
-}
-
-void serverTask(void *params) {
+void serverTask(void *params)
+{
// In the separate task we first do everything that we would have done in the
// setup() function, if we would run the server synchronously.
@@ -112,11 +74,14 @@ void serverTask(void *params) {
Serial.println("Starting server...");
secureServer.start();
- if (secureServer.isRunning()) {
+
+ if (secureServer.isRunning())
+ {
Serial.println("Server ready.");
// "loop()" function of the separate task
- while(true) {
+ while (true)
+ {
// This call will let the server do its work
secureServer.loop();
@@ -126,7 +91,8 @@ void serverTask(void *params) {
}
}
-void handleRoot(HTTPRequest * req, HTTPResponse * res) {
+void handleRoot(HTTPRequest * req, HTTPResponse * res)
+{
// Status code is 200 OK by default.
// We want to deliver a simple HTML page, so we send a corresponding content type:
res->setHeader("Content-Type", "text/html");
@@ -140,13 +106,14 @@ void handleRoot(HTTPRequest * req, HTTPResponse * res) {
res->println("
Hello World!
");
res->print("
Your server is running for ");
// A bit of dynamic data: Show the uptime
- res->print((int)(millis()/1000), DEC);
+ res->print((int)(millis() / 1000), DEC);
res->println(" seconds.
");
res->println("");
res->println("");
}
-void handle404(HTTPRequest * req, HTTPResponse * res) {
+void handle404(HTTPRequest * req, HTTPResponse * res)
+{
// Discard request body, if we received any
// We do this, as this is the default node and may also server POST/PUT requests
req->discardRequestBody();
@@ -165,3 +132,44 @@ void handle404(HTTPRequest * req, HTTPResponse * res) {
res->println("
404 Not Found
The requested resource was not found on this server.
");
res->println("");
}
+
+void setup()
+{
+ // For logging
+ Serial.begin(115200);
+ while (!Serial && millis() < 5000);
+
+ ///////////////////////////////////////////////
+
+ Serial.print("\nStarting Async_Server on "); Serial.println(ARDUINO_BOARD);
+
+ // Connect to WiFi
+ Serial.println("Setting up WiFi");
+ WiFi.begin(WIFI_SSID, WIFI_PSK);
+
+ while (WiFi.status() != WL_CONNECTED)
+ {
+ Serial.print(".");
+ delay(500);
+ }
+
+ Serial.print("Connected. IP=");
+ Serial.println(WiFi.localIP());
+
+ // Setup the server as a separate task.
+ Serial.println("Creating server task... ");
+ // We pass:
+ // serverTask - the function that should be run as separate task
+ // "https443" - a name for the task (mainly used for logging)
+ // 6144 - stack size in byte. If you want up to four clients, you should
+ // not go below 6kB. If your stack is too small, you will encounter
+ // Panic and stack canary exceptions, usually during the call to
+ // SSL_accept.
+ xTaskCreatePinnedToCore(serverTask, "https443", 6144, NULL, 1, NULL, ARDUINO_RUNNING_CORE);
+}
+
+void loop()
+{
+ Serial.println("loop()");
+ delay(5000);
+}
diff --git a/examples/Async-Server/cert.h b/examples/Async-Server/cert.h
new file mode 100644
index 0000000..c4b5c43
--- /dev/null
+++ b/examples/Async-Server/cert.h
@@ -0,0 +1,4 @@
+#ifndef CERT_H_
+#define CERT_H_
+ #error You have to run the srcipt extras/create_cert.sh to recreate these files
+#endif
diff --git a/examples/Async-Server/private_key.h b/examples/Async-Server/private_key.h
new file mode 100644
index 0000000..ec2498e
--- /dev/null
+++ b/examples/Async-Server/private_key.h
@@ -0,0 +1,4 @@
+#ifndef PRIVATE_KEY_H_
+#define PRIVATE_KEY_H_
+ #error You have to run the srcipt extras/create_cert.sh to recreate these files
+#endif
diff --git a/examples/Authentication/Authentication.ino b/examples/Authentication/Authentication.ino
index 0a1cb2d..a66506d 100644
--- a/examples/Authentication/Authentication.ino
+++ b/examples/Authentication/Authentication.ino
@@ -1,28 +1,28 @@
/**
- * Example for the ESP32 HTTP(S) Webserver
- *
- * IMPORTANT NOTE:
- * To run this script, your need to
- * 1) Enter your WiFi SSID and PSK below this comment
- * 2) Make sure to have certificate data available. You will find a
- * shell script and instructions to do so in the library folder
- * under extras/
- *
- * This script will install an HTTPS Server on your ESP32 with the following
- * functionalities:
- * - Show simple page on web server root
- * - Provide some "internal pages" that are protected by the server
- * - Run a middleware that authenticates the user
- * - Run a middleware that provides access control
- * - 404 for everything else
- * Authentication is done using HTTP Basic Auth, which is supported by the webserver,
- * so you don't have to care about retrieving the login information from request
- * headers.
- */
+ Example for the ESP32 HTTP(S) Webserver
+
+ IMPORTANT NOTE:
+ To run this script, your need to
+ 1) Enter your WiFi SSID and PSK below this comment
+ 2) Make sure to have certificate data available. You will find a
+ shell script and instructions to do so in the library folder
+ under extras/
+
+ This script will install an HTTPS Server on your ESP32 with the following
+ functionalities:
+ - Show simple page on web server root
+ - Provide some "internal pages" that are protected by the server
+ - Run a middleware that authenticates the user
+ - Run a middleware that provides access control
+ - 404 for everything else
+ Authentication is done using HTTP Basic Auth, which is supported by the webserver,
+ so you don't have to care about retrieving the login information from request
+ headers.
+*/
// TODO: Configure your WiFi here
-#define WIFI_SSID ""
-#define WIFI_PSK ""
+#define WIFI_SSID "your_ssid"
+#define WIFI_PSK "12345678"
// Include certificate data (see note above)
#include "cert.h"
@@ -51,21 +51,14 @@ using namespace httpsserver;
// Create an SSL certificate object from the files included above
SSLCert cert = SSLCert(
- example_crt_DER, example_crt_DER_len,
- example_key_DER, example_key_DER_len
-);
+ example_crt_DER, example_crt_DER_len,
+ example_key_DER, example_key_DER_len
+ );
// Create an SSL-enabled server that uses the certificate
// The contstructor takes some more parameters, but we go for default values here.
HTTPSServer secureServer = HTTPSServer(&cert);
-// Declare some handler functions for the various URLs on the server
-void handleRoot(HTTPRequest * req, HTTPResponse * res);
-void handleInternalPage(HTTPRequest * req, HTTPResponse * res);
-void handleAdminPage(HTTPRequest * req, HTTPResponse * res);
-void handlePublicPage(HTTPRequest * req, HTTPResponse * res);
-void handle404(HTTPRequest * req, HTTPResponse * res);
-
// Declare a middleware function.
// Parameters:
// req: Request data, can be used to access URL, HTTP Method, Headers, ...
@@ -78,79 +71,23 @@ void handle404(HTTPRequest * req, HTTPResponse * res);
// code 403 on the response to show that the user is not allowed to access a specific
// resource.
// For more details, see the definition below.
-void middlewareAuthentication(HTTPRequest * req, HTTPResponse * res, std::function next);
-void middlewareAuthorization(HTTPRequest * req, HTTPResponse * res, std::function next);
-
-void setup() {
- // For logging
- Serial.begin(115200);
-
- // Connect to WiFi
- Serial.println("Setting up WiFi");
- WiFi.begin(WIFI_SSID, WIFI_PSK);
- while (WiFi.status() != WL_CONNECTED) {
- Serial.print(".");
- delay(500);
- }
- Serial.print("Connected. IP=");
- Serial.println(WiFi.localIP());
-
- // For every resource available on the server, we need to create a ResourceNode
- // The ResourceNode links URL and HTTP method to a handler function
- ResourceNode * nodeRoot = new ResourceNode("/", "GET", &handleRoot);
- ResourceNode * nodeInternal = new ResourceNode("/internal", "GET", &handleInternalPage);
- ResourceNode * nodeAdmin = new ResourceNode("/internal/admin", "GET", &handleAdminPage);
- ResourceNode * nodePublic = new ResourceNode("/public", "GET", &handlePublicPage);
- ResourceNode * node404 = new ResourceNode("", "GET", &handle404);
-
- // Add the nodes to the server
- secureServer.registerNode(nodeRoot);
- secureServer.registerNode(nodeInternal);
- secureServer.registerNode(nodeAdmin);
- secureServer.registerNode(nodePublic);
-
- // Add the 404 not found node to the server.
- // The path is ignored for the default node.
- secureServer.setDefaultNode(node404);
-
- // Add the middleware. These functions will be called globally for every request
- // Note: The functions are called in the order they are added to the server.
- // This means, we need to add the authentication middleware first, because the
- // authorization middleware needs the headers that will be set by the authentication
- // middleware (First we check the identity, then we see what the user is allowed to do)
- secureServer.addMiddleware(&middlewareAuthentication);
- secureServer.addMiddleware(&middlewareAuthorization);
- Serial.println("Starting server...");
- secureServer.start();
- if (secureServer.isRunning()) {
- Serial.println("Server ready.");
- }
-}
+/**
+ The following middleware function is one of two functions dealing with access control. The
+ middlewareAuthentication() will interpret the HTTP Basic Auth header, check usernames and password,
+ and if they are valid, set the X-USERNAME and X-GROUP header.
-void loop() {
- // This call will let the server do its work
- secureServer.loop();
+ If they are invalid, the X-USERNAME and X-GROUP header will be unset. This is important because
+ otherwise the client may manipulate those internal headers.
- // Other code would go here...
- delay(1);
-}
+ Having that done, further middleware functions and the request handler functions will be able to just
+ use req->getHeader("X-USERNAME") to find out if the user is logged in correctly.
-/**
- * The following middleware function is one of two functions dealing with access control. The
- * middlewareAuthentication() will interpret the HTTP Basic Auth header, check usernames and password,
- * and if they are valid, set the X-USERNAME and X-GROUP header.
- *
- * If they are invalid, the X-USERNAME and X-GROUP header will be unset. This is important because
- * otherwise the client may manipulate those internal headers.
- *
- * Having that done, further middleware functions and the request handler functions will be able to just
- * use req->getHeader("X-USERNAME") to find out if the user is logged in correctly.
- *
- * Furthermore, if the user supplies credentials and they are invalid, he will receive an 401 response
- * without any other functions being called.
- */
-void middlewareAuthentication(HTTPRequest * req, HTTPResponse * res, std::function next) {
+ Furthermore, if the user supplies credentials and they are invalid, he will receive an 401 response
+ without any other functions being called.
+*/
+void middlewareAuthentication(HTTPRequest * req, HTTPResponse * res, std::function next)
+{
// Unset both headers to discard any value from the client
// This prevents authentication bypass by a client that just sets X-USERNAME
req->setHeader(HEADER_USERNAME, "");
@@ -165,21 +102,29 @@ void middlewareAuthentication(HTTPRequest * req, HTTPResponse * res, std::functi
std::string reqPassword = req->getBasicAuthPassword();
// If the user entered login information, we will check it
- if (reqUsername.length() > 0 && reqPassword.length() > 0) {
+ if (reqUsername.length() > 0 && reqPassword.length() > 0)
+ {
// _Very_ simple hardcoded user database to check credentials and assign the group
bool authValid = true;
std::string group = "";
- if (reqUsername == "admin" && reqPassword == "secret") {
+
+ if (reqUsername == "admin" && reqPassword == "secret")
+ {
group = "ADMIN";
- } else if (reqUsername == "user" && reqPassword == "test") {
+ }
+ else if (reqUsername == "user" && reqPassword == "test")
+ {
group = "USER";
- } else {
+ }
+ else
+ {
authValid = false;
}
// If authentication was successful
- if (authValid) {
+ if (authValid)
+ {
// set custom headers and delegate control
req->setHeader(HEADER_USERNAME, reqUsername);
req->setHeader(HEADER_GROUP, group);
@@ -187,7 +132,9 @@ void middlewareAuthentication(HTTPRequest * req, HTTPResponse * res, std::functi
// The user tried to authenticate and was successful
// -> We proceed with this request.
next();
- } else {
+ }
+ else
+ {
// Display error page
res->setStatusCode(401);
res->setStatusText("Unauthorized");
@@ -204,7 +151,9 @@ void middlewareAuthentication(HTTPRequest * req, HTTPResponse * res, std::functi
// NO CALL TO next() here, as the authentication failed.
// -> The code above did handle the request already.
}
- } else {
+ }
+ else
+ {
// No attempt to authenticate
// -> Let the request pass through by calling next()
next();
@@ -212,19 +161,21 @@ void middlewareAuthentication(HTTPRequest * req, HTTPResponse * res, std::functi
}
/**
- * This function plays together with the middlewareAuthentication(). While the first function checks the
- * username/password combination and stores it in the request, this function makes use of this information
- * to allow or deny access.
- *
- * This example only prevents unauthorized access to every ResourceNode stored under an /internal/... path.
- */
-void middlewareAuthorization(HTTPRequest * req, HTTPResponse * res, std::function next) {
+ This function plays together with the middlewareAuthentication(). While the first function checks the
+ username/password combination and stores it in the request, this function makes use of this information
+ to allow or deny access.
+
+ This example only prevents unauthorized access to every ResourceNode stored under an /internal/... path.
+*/
+void middlewareAuthorization(HTTPRequest * req, HTTPResponse * res, std::function next)
+{
// Get the username (if any)
std::string username = req->getHeader(HEADER_USERNAME);
// Check that only logged-in users may get to the internal area (All URLs starting with /internal)
// Only a simple example, more complicated configuration is up to you.
- if (username == "" && req->getRequestString().substr(0,9) == "/internal") {
+ if (username == "" && req->getRequestString().substr(0, 9) == "/internal")
+ {
// Same as the deny-part in middlewareAuthentication()
res->setStatusCode(401);
res->setStatusText("Unauthorized");
@@ -233,7 +184,9 @@ void middlewareAuthorization(HTTPRequest * req, HTTPResponse * res, std::functio
res->println("401. Unauthorized (try admin/secret or user/test)");
// No call denies access to protected handler function.
- } else {
+ }
+ else
+ {
// Everything else will be allowed, so we call next()
next();
}
@@ -242,7 +195,8 @@ void middlewareAuthorization(HTTPRequest * req, HTTPResponse * res, std::functio
// This is the internal page. It will greet the user with
// a personalized message and - if the user is in the ADMIN group -
// provide a link to the admin interface.
-void handleInternalPage(HTTPRequest * req, HTTPResponse * res) {
+void handleInternalPage(HTTPRequest * req, HTTPResponse * res)
+{
// Header
res->setStatusCode(200);
res->setStatusText("OK");
@@ -266,7 +220,8 @@ void handleInternalPage(HTTPRequest * req, HTTPResponse * res) {
res->println("
Welcome to the internal area. Congratulations on successfully entering your password!
");
// The "admin area" will only be shown if the correct group has been assigned in the authenticationMiddleware
- if (req->getHeader(HEADER_GROUP) == "ADMIN") {
+ if (req->getHeader(HEADER_GROUP) == "ADMIN")
+ {
res->println("
");
res->println("
You are an administrator
");
res->println("
You are allowed to access the admin page:
");
@@ -280,7 +235,8 @@ void handleInternalPage(HTTPRequest * req, HTTPResponse * res) {
res->println("");
}
-void handleAdminPage(HTTPRequest * req, HTTPResponse * res) {
+void handleAdminPage(HTTPRequest * req, HTTPResponse * res)
+{
// Headers
res->setHeader("Content-Type", "text/html; charset=utf8");
@@ -289,7 +245,8 @@ void handleAdminPage(HTTPRequest * req, HTTPResponse * res) {
// Checking permissions can not only be done centrally in the middleware function but also in the actual request handler.
// This would be handy if you provide an API with lists of resources, but access rights are defined object-based.
- if (req->getHeader(HEADER_GROUP) == "ADMIN") {
+ if (req->getHeader(HEADER_GROUP) == "ADMIN")
+ {
res->setStatusCode(200);
res->setStatusText("OK");
res->printStd(header);
@@ -298,7 +255,9 @@ void handleAdminPage(HTTPRequest * req, HTTPResponse * res) {
res->println("
The requested resource was not found on this server.
");
res->println("");
}
+
+void setup()
+{
+ // For logging
+ Serial.begin(115200);
+ while (!Serial && millis() < 5000);
+
+ ///////////////////////////////////////////////
+
+ Serial.print("\nStarting Authentication on "); Serial.println(ARDUINO_BOARD);
+
+ // Connect to WiFi
+ Serial.println("Setting up WiFi");
+ WiFi.begin(WIFI_SSID, WIFI_PSK);
+
+ while (WiFi.status() != WL_CONNECTED)
+ {
+ Serial.print(".");
+ delay(500);
+ }
+
+ Serial.print("Connected. IP=");
+ Serial.println(WiFi.localIP());
+
+ // For every resource available on the server, we need to create a ResourceNode
+ // The ResourceNode links URL and HTTP method to a handler function
+ ResourceNode * nodeRoot = new ResourceNode("/", "GET", &handleRoot);
+ ResourceNode * nodeInternal = new ResourceNode("/internal", "GET", &handleInternalPage);
+ ResourceNode * nodeAdmin = new ResourceNode("/internal/admin", "GET", &handleAdminPage);
+ ResourceNode * nodePublic = new ResourceNode("/public", "GET", &handlePublicPage);
+ ResourceNode * node404 = new ResourceNode("", "GET", &handle404);
+
+ // Add the nodes to the server
+ secureServer.registerNode(nodeRoot);
+ secureServer.registerNode(nodeInternal);
+ secureServer.registerNode(nodeAdmin);
+ secureServer.registerNode(nodePublic);
+
+ // Add the 404 not found node to the server.
+ // The path is ignored for the default node.
+ secureServer.setDefaultNode(node404);
+
+ // Add the middleware. These functions will be called globally for every request
+ // Note: The functions are called in the order they are added to the server.
+ // This means, we need to add the authentication middleware first, because the
+ // authorization middleware needs the headers that will be set by the authentication
+ // middleware (First we check the identity, then we see what the user is allowed to do)
+ secureServer.addMiddleware(&middlewareAuthentication);
+ secureServer.addMiddleware(&middlewareAuthorization);
+
+ Serial.println("Starting server...");
+ secureServer.start();
+
+ if (secureServer.isRunning())
+ {
+ Serial.println("Server ready.");
+ }
+}
+
+void loop()
+{
+ // This call will let the server do its work
+ secureServer.loop();
+
+ // Other code would go here...
+ delay(1);
+}
diff --git a/examples/Authentication/cert.h b/examples/Authentication/cert.h
new file mode 100644
index 0000000..c4b5c43
--- /dev/null
+++ b/examples/Authentication/cert.h
@@ -0,0 +1,4 @@
+#ifndef CERT_H_
+#define CERT_H_
+ #error You have to run the srcipt extras/create_cert.sh to recreate these files
+#endif
diff --git a/examples/Authentication/private_key.h b/examples/Authentication/private_key.h
new file mode 100644
index 0000000..ec2498e
--- /dev/null
+++ b/examples/Authentication/private_key.h
@@ -0,0 +1,4 @@
+#ifndef PRIVATE_KEY_H_
+#define PRIVATE_KEY_H_
+ #error You have to run the srcipt extras/create_cert.sh to recreate these files
+#endif
diff --git a/examples/HTML-Forms/HTML-Forms.ino b/examples/HTML-Forms/HTML-Forms.ino
index b29dfba..d4ca00f 100644
--- a/examples/HTML-Forms/HTML-Forms.ino
+++ b/examples/HTML-Forms/HTML-Forms.ino
@@ -1,24 +1,24 @@
/**
- * Example for the ESP32 HTTP(S) Webserver
- *
- * IMPORTANT NOTE:
- * To run this script, you need to
- * 1) Enter your WiFi SSID and PSK below this comment
- * 2) Make sure to have certificate data available. You will find a
- * shell script and instructions to do so in the library folder
- * under extras/
- *
- * This script will install an HTTPS Server on your ESP32 with the following
- * functionalities:
- * - Show simple page on web server root that includes some HTML Forms
- * - Define a POST handler that handles the forms using the HTTPBodyParser API
- * provided by the library.
- * - 404 for everything else
- */
+ Example for the ESP32 HTTP(S) Webserver
+
+ IMPORTANT NOTE:
+ To run this script, you need to
+ 1) Enter your WiFi SSID and PSK below this comment
+ 2) Make sure to have certificate data available. You will find a
+ shell script and instructions to do so in the library folder
+ under extras/
+
+ This script will install an HTTPS Server on your ESP32 with the following
+ functionalities:
+ - Show simple page on web server root that includes some HTML Forms
+ - Define a POST handler that handles the forms using the HTTPBodyParser API
+ provided by the library.
+ - 404 for everything else
+*/
// TODO: Configure your WiFi here
-#define WIFI_SSID ""
-#define WIFI_PSK ""
+#define WIFI_SSID "your_ssid"
+#define WIFI_PSK "12345678"
// Include certificate data (see note above)
#include "cert.h"
@@ -42,11 +42,11 @@
// We need to specify some content-type mapping, so the resources get delivered with the
// right content type and are displayed correctly in the browser
-char contentTypes[][2][32] = {
- {".txt", "text/plain"},
- {".html", "text/html"},
- {".png", "image/png"},
- {".jpg", "image/jpg"},
+char contentTypes[][2][32] =
+{
+ {".txt", "text/plain"},
+ {".png", "image/png"},
+ {".jpg", "image/jpg"},
{"", ""}
};
@@ -55,33 +55,31 @@ using namespace httpsserver;
// Create an SSL certificate object from the files included above
SSLCert cert = SSLCert(
- example_crt_DER, example_crt_DER_len,
- example_key_DER, example_key_DER_len
-);
+ example_crt_DER, example_crt_DER_len,
+ example_key_DER, example_key_DER_len
+ );
// Create an SSL-enabled server that uses the certificate
// The contstructor takes some more parameters, but we go for default values here.
HTTPSServer secureServer = HTTPSServer(&cert);
-// Declare some handler functions for the various URLs on the server
-// See the static-page example for how handler functions work.
-// The comments in setup() describe what each handler function does in this example.
-void handleRoot(HTTPRequest * req, HTTPResponse * res);
-void handleFormUpload(HTTPRequest * req, HTTPResponse * res);
-void handleFormEdit(HTTPRequest * req, HTTPResponse * res);
-void handleFile(HTTPRequest * req, HTTPResponse * res);
-void handleDirectory(HTTPRequest * req, HTTPResponse * res);
-void handle404(HTTPRequest * req, HTTPResponse * res);
-
-// As we have a file editor where the content of a file is pasted into a