| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,198 @@ | |||
| 1 | + /** | ||
| 2 | + * Example for the ESP32 HTTP(S) Webserver | ||
| 3 | + * | ||
| 4 | + * IMPORTANT NOTE: | ||
| 5 | + * To run this script, you need to | ||
| 6 | + * 1) Enter your WiFi SSID and PSK below this comment | ||
| 7 | + * 2) Make sure to have certificate data available. You will find a | ||
| 8 | + * shell script and instructions to do so in the library folder | ||
| 9 | + * under extras/ | ||
| 10 | + * | ||
| 11 | + * This script will install an HTTPS Server on your ESP32 with the following | ||
| 12 | + * functionalities: | ||
| 13 | + * - Show simple page on web server root that includes some HTML Forms | ||
| 14 | + * - Define a POST handler that handles the forms using the HTTPBodyParser API | ||
| 15 | + * provided by the library. | ||
| 16 | + * - 404 for everything else | ||
| 17 | + */ | ||
| 18 | + | ||
| 19 | + // TODO: Configure your WiFi here | ||
| 20 | + #define WIFI_SSID "<your ssid goes here>" | ||
| 21 | + #define WIFI_PSK "<your pre-shared key goes here>" | ||
| 22 | + | ||
| 23 | + // Include certificate data (see note above) | ||
| 24 | + #include "cert.h" | ||
| 25 | + #include "private_key.h" | ||
| 26 | + | ||
| 27 | + // We will use wifi | ||
| 28 | + #include <WiFi.h> | ||
| 29 | + | ||
| 30 | + // Includes for the server | ||
| 31 | + #include <HTTPSServer.hpp> | ||
| 32 | + #include <SSLCert.hpp> | ||
| 33 | + #include <HTTPRequest.hpp> | ||
| 34 | + #include <HTTPResponse.hpp> | ||
| 35 | + | ||
| 36 | + // The HTTPS Server comes in a separate namespace. For easier use, include it here. | ||
| 37 | + using namespace httpsserver; | ||
| 38 | + | ||
| 39 | + // Create an SSL certificate object from the files included above | ||
| 40 | + SSLCert cert = SSLCert( | ||
| 41 | + example_crt_DER, example_crt_DER_len, | ||
| 42 | + example_key_DER, example_key_DER_len | ||
| 43 | + ); | ||
| 44 | + | ||
| 45 | + // Create an SSL-enabled server that uses the certificate | ||
| 46 | + // The contstructor takes some more parameters, but we go for default values here. | ||
| 47 | + HTTPSServer secureServer = HTTPSServer(&cert); | ||
| 48 | + | ||
| 49 | + // Declare some handler functions for the various URLs on the server | ||
| 50 | + // The signature is always the same for those functions. They get two parameters, | ||
| 51 | + // which are pointers to the request data (read request body, headers, ...) and | ||
| 52 | + // to the response data (write response, set status code, ...) | ||
| 53 | + void handleRoot(HTTPRequest * req, HTTPResponse * res); | ||
| 54 | + void handleForm(HTTPRequest * req, HTTPResponse * res); | ||
| 55 | + void handle404(HTTPRequest * req, HTTPResponse * res); | ||
| 56 | + | ||
| 57 | + void setup() { | ||
| 58 | + // For logging | ||
| 59 | + Serial.begin(115200); | ||
| 60 | + | ||
| 61 | + // Connect to WiFi | ||
| 62 | + Serial.println("Setting up WiFi"); | ||
| 63 | + WiFi.begin(WIFI_SSID, WIFI_PSK); | ||
| 64 | + while (WiFi.status() != WL_CONNECTED) { | ||
| 65 | + Serial.print("."); | ||
| 66 | + delay(500); | ||
| 67 | + } | ||
| 68 | + Serial.print("Connected. IP="); | ||
| 69 | + Serial.println(WiFi.localIP()); | ||
| 70 | + | ||
| 71 | + // For every resource available on the server, we need to create a ResourceNode | ||
| 72 | + // The ResourceNode links URL and HTTP method to a handler function | ||
| 73 | + ResourceNode * nodeRoot = new ResourceNode("/", "GET", &handleRoot); | ||
| 74 | + ResourceNode * nodeForm = new ResourceNode("/", "POST", &handleForm); | ||
| 75 | + | ||
| 76 | + // 404 node has no URL as it is used for all requests that don't match anything else | ||
| 77 | + ResourceNode * node404 = new ResourceNode("", "GET", &handle404); | ||
| 78 | + | ||
| 79 | + // Add the root nodes to the server | ||
| 80 | + secureServer.registerNode(nodeRoot); | ||
| 81 | + secureServer.registerNode(nodeForm); | ||
| 82 | + | ||
| 83 | + // Add the 404 not found node to the server. | ||
| 84 | + // The path is ignored for the default node. | ||
| 85 | + secureServer.setDefaultNode(node404); | ||
| 86 | + | ||
| 87 | + Serial.println("Starting server..."); | ||
| 88 | + secureServer.start(); | ||
| 89 | + if (secureServer.isRunning()) { | ||
| 90 | + Serial.println("Server ready."); | ||
| 91 | + } | ||
| 92 | + } | ||
| 93 | + | ||
| 94 | + void loop() { | ||
| 95 | + // This call will let the server do its work | ||
| 96 | + secureServer.loop(); | ||
| 97 | + | ||
| 98 | + // Other code would go here... | ||
| 99 | + delay(1); | ||
| 100 | + } | ||
| 101 | + | ||
| 102 | + void handleRoot(HTTPRequest * req, HTTPResponse * res) { | ||
| 103 | + // Status code is 200 OK by default. | ||
| 104 | + // We want to deliver a simple HTML page, so we send a corresponding content type: | ||
| 105 | + res->setHeader("Content-Type", "text/html"); | ||
| 106 | + | ||
| 107 | + // The response implements the Print interface, so you can use it just like | ||
| 108 | + // you would write to Serial etc. | ||
| 109 | + res->println("<!DOCTYPE html>"); | ||
| 110 | + res->println("<html>"); | ||
| 111 | + res->println("<head><title>Hello World!</title></head>"); | ||
| 112 | + res->println("<body>"); | ||
| 113 | + res->println("<h1>HTML Forms</h1>"); | ||
| 114 | + res->println("<p>This page contains some forms to show you how form data can be evaluated on server side."); | ||
| 115 | + | ||
| 116 | + // The following forms will all use the same target (/ - the server's root directory) and POST method, so | ||
| 117 | + // the data will go to the request body. They differ on the value of the enctype though. | ||
| 118 | + | ||
| 119 | + // enctype=x-www-form-urlencoded | ||
| 120 | + // means that the parameters of form elements will be encoded like they would | ||
| 121 | + // be encoded if you would use GET as method and append them to the URL (just after a ? at the end of the | ||
| 122 | + // resource path). Different fields are separated by an &-character. Special characters have a specific encoding | ||
| 123 | + // using the %-character, so for example %20 is the representation of a space. | ||
| 124 | + // The body could look like this: | ||
| 125 | + // | ||
| 126 | + // foo=bar&bat=baz | ||
| 127 | + // | ||
| 128 | + // Where foo and bat are the variables and bar and baz the values. | ||
| 129 | + // | ||
| 130 | + // Advantages: | ||
| 131 | + // - Low overhead | ||
| 132 | + // Disadvantages: | ||
| 133 | + // - May be hard to read for humans | ||
| 134 | + // - Cannot be used for inputs with type=file (you will only get the filename, not the content) | ||
| 135 | + res->println("<form method=\"POST\" action=\"/\" enctype=\"x-www-form-urlencoded\">"); | ||
| 136 | + res->println("<h2>enctype=x-www-form-urlencoded</h2>"); | ||
| 137 | + res->println("</form>") | ||
| 138 | + | ||
| 139 | + // enctype=multipart/form-data | ||
| 140 | + // | ||
| 141 | + // TODO: Explanatory text | ||
| 142 | + // | ||
| 143 | + // Advantages: | ||
| 144 | + // - Even longer text stays somewhat human readable | ||
| 145 | + // - Can be used for files and binary data | ||
| 146 | + // Disadvantages: | ||
| 147 | + // - Big overhead if used for some small string values | ||
| 148 | + res->println("<form method=\"POST\" action=\"/\" enctype=\"multipart/form-data\">"); | ||
| 149 | + res->println("<h2>enctype=multipart/form-data</h2>"); | ||
| 150 | + res->println("</form>") | ||
| 151 | + | ||
| 152 | + res->println("</body>"); | ||
| 153 | + res->println("</html>"); | ||
| 154 | + } | ||
| 155 | + | ||
| 156 | + void handleForm(HTTPRequest * req, HTTPResponse * res) { | ||
| 157 | + // First, we need to check the encoding of the form that we have received. | ||
| 158 | + // The browser will set the Content-Type request header, so we can use it for that purpose. | ||
| 159 | + // Then we select the body parser based on the encoding. | ||
| 160 | + // Note: This is only necessary if you expect various enctypes to be send to the same handler. | ||
| 161 | + // If you would have only one form on your page with a fixed enctype, just instantiate the | ||
| 162 | + // corresponding reader. | ||
| 163 | + | ||
| 164 | + // TODO: Select Parser, instantiate it | ||
| 165 | + | ||
| 166 | + // Now we iterate over the so-called fields of the BodyParser. This works in the same way for | ||
| 167 | + // all body parsers. | ||
| 168 | + // The interface is somewhat limited, so you cannot just call something like | ||
| 169 | + // myParser.get("fieldname"), because this would require the parser to cache the whole request | ||
| 170 | + // body. If you have your ESP32 attached to an external SD Card and you want to be able to upload | ||
| 171 | + // files that are larger than the ESP's memory to that card, this would not work. | ||
| 172 | + // This is why you iterate over the fields by using myParser.next() and check the name of the | ||
| 173 | + // current field with myParser.getFieldName(), and then process it with a buffer. | ||
| 174 | + // If you need random access to all fields' values, you need to parse them into a map or some similar | ||
| 175 | + // data structure by yourself and make sure that all fits into the memory. | ||
| 176 | + | ||
| 177 | + // TODO: Iterate over fields | ||
| 178 | + } | ||
| 179 | + | ||
| 180 | + void handle404(HTTPRequest * req, HTTPResponse * res) { | ||
| 181 | + // Discard request body, if we received any | ||
| 182 | + // We do this, as this is the default node and may also server POST/PUT requests | ||
| 183 | + req->discardRequestBody(); | ||
| 184 | + | ||
| 185 | + // Set the response status | ||
| 186 | + res->setStatusCode(404); | ||
| 187 | + res->setStatusText("Not Found"); | ||
| 188 | + | ||
| 189 | + // Set content type of the response | ||
| 190 | + res->setHeader("Content-Type", "text/html"); | ||
| 191 | + | ||
| 192 | + // Write a tiny HTML page | ||
| 193 | + res->println("<!DOCTYPE html>"); | ||
| 194 | + res->println("<html>"); | ||
| 195 | + res->println("<head><title>Not Found</title></head>"); | ||
| 196 | + res->println("<body><h1>404 Not Found</h1><p>The requested resource was not found on this server.</p></body>"); | ||
| 197 | + res->println("</html>"); | ||
| 198 | + } | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,62 @@ | |||
| 1 | + #ifndef SRC_HTTPBODYPARSER_HPP_ | ||
| 2 | + #define SRC_HTTPBODYPARSER_HPP_ | ||
| 3 | + | ||
| 4 | + #include <Arduino.h> | ||
| 5 | + #include <string> | ||
| 6 | + #include "HTTPRequest.hpp" | ||
| 7 | + | ||
| 8 | + namespace httpsserver { | ||
| 9 | + | ||
| 10 | + /** | ||
| 11 | + * Superclass for various body parser implementations that can be used to | ||
| 12 | + * interpret http-specific bodies (like x-www-form-urlencoded or multipart/form-data) | ||
| 13 | + * | ||
| 14 | + * To allow for arbitrary body length, the interface of the body parser provides access | ||
| 15 | + * to one underlying "field" at a time. A field may be a value of the urlencoded string | ||
| 16 | + * or a part of a multipart message. | ||
| 17 | + * | ||
| 18 | + * Using next() proceeds to the next field. | ||
| 19 | + */ | ||
| 20 | + class HTTPBodyParser { | ||
| 21 | + public: | ||
| 22 | + HTTPBodyParser(HTTPRequest * req): _request(req) {}; | ||
| 23 | + | ||
| 24 | + /** | ||
| 25 | + * Proceeds to the next field of the body | ||
| 26 | + * | ||
| 27 | + * If a field has not been read completely, the remaining content is discarded. | ||
| 28 | + * | ||
| 29 | + * Returns true iff proceeding to the next field succeeded (ie there was a next field) | ||
| 30 | + */ | ||
| 31 | + virtual bool nextField() = 0; | ||
| 32 | + | ||
| 33 | + /** Returns the name of the current field */ | ||
| 34 | + virtual std::string getFieldName() = 0; | ||
| 35 | + | ||
| 36 | + /** | ||
| 37 | + * Returns the mime type of the current field. | ||
| 38 | + * | ||
| 39 | + * Note: This value is set by the client. It can be altered maliciously. Do NOT rely on it | ||
| 40 | + * for anything that affects the security of your device or other clients connected to it! | ||
| 41 | + * | ||
| 42 | + * Not every BodyParser might provide this value, usually it's set to something like text/plain then | ||
| 43 | + */ | ||
| 44 | + virtual std::string getFieldMimeType() = 0; | ||
| 45 | + | ||
| 46 | + /** Returns the total length of the field */ | ||
| 47 | + virtual size_t getLength() = 0; | ||
| 48 | + | ||
| 49 | + /** Returns the remaining length of the field. 0 means the field has been read completely */ | ||
| 50 | + virtual size_t getRemainingLength() = 0; | ||
| 51 | + | ||
| 52 | + /** Reads a maximum of bufferSize bytes into buffer and returns the actual amount of bytes that have been read */ | ||
| 53 | + virtual size_t read(byte* buffer, size_t bufferSize) = 0; | ||
| 54 | + | ||
| 55 | + protected: | ||
| 56 | + /** The underlying request */ | ||
| 57 | + HTTPRequest * _request; | ||
| 58 | + }; | ||
| 59 | + | ||
| 60 | + } // namespace httpserver | ||
| 61 | + | ||
| 62 | + #endif | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,30 @@ | |||
| 1 | + #include "HTTPMultipartBodyParser.hpp" | ||
| 2 | + | ||
| 3 | + namespace httpsserver { | ||
| 4 | + | ||
| 5 | + bool HTTPMultipartBodyParser::nextField() { | ||
| 6 | + return false; | ||
| 7 | + } | ||
| 8 | + | ||
| 9 | + std::string HTTPMultipartBodyParser::getFieldName() { | ||
| 10 | + return std::string("foo"); | ||
| 11 | + } | ||
| 12 | + | ||
| 13 | + std::string HTTPMultipartBodyParser::getFieldMimeType() { | ||
| 14 | + return std::string("text/plain"); | ||
| 15 | + } | ||
| 16 | + | ||
| 17 | + size_t HTTPMultipartBodyParser::getLength() { | ||
| 18 | + return 0; | ||
| 19 | + } | ||
| 20 | + | ||
| 21 | + size_t HTTPMultipartBodyParser::getRemainingLength() { | ||
| 22 | + return 0; | ||
| 23 | + } | ||
| 24 | + | ||
| 25 | + size_t HTTPMultipartBodyParser::read(byte* buffer, size_t bufferSize) { | ||
| 26 | + return 0; | ||
| 27 | + } | ||
| 28 | + | ||
| 29 | + | ||
| 30 | + } | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,22 @@ | |||
| 1 | + #ifndef SRC_HTTPMULTIPARTBODYPARSER_HPP_ | ||
| 2 | + #define SRC_HTTPMULTIPARTBODYPARSER_HPP_ | ||
| 3 | + | ||
| 4 | + #include <Arduino.h> | ||
| 5 | + #include "HTTPBodyParser.hpp" | ||
| 6 | + | ||
| 7 | + namespace httpsserver { | ||
| 8 | + | ||
| 9 | + class HTTPMultipartBodyParser : public HTTPBodyParser { | ||
| 10 | + public: | ||
| 11 | + // From HTTPBodyParser | ||
| 12 | + virtual bool nextField(); | ||
| 13 | + virtual std::string getFieldName(); | ||
| 14 | + virtual std::string getFieldMimeType(); | ||
| 15 | + virtual size_t getLength(); | ||
| 16 | + virtual size_t getRemainingLength(); | ||
| 17 | + virtual size_t read(byte* buffer, size_t bufferSize); | ||
| 18 | + }; | ||
| 19 | + | ||
| 20 | + } // namespace httpserver | ||
| 21 | + | ||
| 22 | + #endif | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,30 @@ | |||
| 1 | + #include "HTTPURLEncodedBodyParser.hpp" | ||
| 2 | + | ||
| 3 | + namespace httpsserver { | ||
| 4 | + | ||
| 5 | + bool HTTPURLEncodedBodyParser::nextField() { | ||
| 6 | + return false; | ||
| 7 | + } | ||
| 8 | + | ||
| 9 | + std::string HTTPURLEncodedBodyParser::getFieldName() { | ||
| 10 | + return std::string("foo"); | ||
| 11 | + } | ||
| 12 | + | ||
| 13 | + std::string HTTPURLEncodedBodyParser::getFieldMimeType() { | ||
| 14 | + return std::string("text/plain"); | ||
| 15 | + } | ||
| 16 | + | ||
| 17 | + size_t HTTPURLEncodedBodyParser::getLength() { | ||
| 18 | + return 0; | ||
| 19 | + } | ||
| 20 | + | ||
| 21 | + size_t HTTPURLEncodedBodyParser::getRemainingLength() { | ||
| 22 | + return 0; | ||
| 23 | + } | ||
| 24 | + | ||
| 25 | + size_t HTTPURLEncodedBodyParser::read(byte* buffer, size_t bufferSize) { | ||
| 26 | + return 0; | ||
| 27 | + } | ||
| 28 | + | ||
| 29 | + | ||
| 30 | + } | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,22 @@ | |||
| 1 | + #ifndef SRC_HTTPURLENCODEDBODYPARSER_HPP_ | ||
| 2 | + #define SRC_HTTPURLENCODEDBODYPARSER_HPP_ | ||
| 3 | + | ||
| 4 | + #include <Arduino.h> | ||
| 5 | + #include "HTTPBodyParser.hpp" | ||
| 6 | + | ||
| 7 | + namespace httpsserver { | ||
| 8 | + | ||
| 9 | + class HTTPURLEncodedBodyParser : public HTTPBodyParser { | ||
| 10 | + public: | ||
| 11 | + // From HTTPBodyParser | ||
| 12 | + virtual bool nextField(); | ||
| 13 | + virtual std::string getFieldName(); | ||
| 14 | + virtual std::string getFieldMimeType(); | ||
| 15 | + virtual size_t getLength(); | ||
| 16 | + virtual size_t getRemainingLength(); | ||
| 17 | + virtual size_t read(byte* buffer, size_t bufferSize); | ||
| 18 | + }; | ||
| 19 | + | ||
| 20 | + } // namespace httpserver | ||
| 21 | + | ||
| 22 | + #endif | ||
| Back | FazBrowse Home | New Git URL |
0 commit comments