Prvni ulozeni z chegewara githubu

This commit is contained in:
2023-02-25 16:13:53 +01:00
commit 01eb80dfe2
3279 changed files with 638407 additions and 0 deletions

View File

@ -0,0 +1,147 @@
/*
Copyright (c) 2015, Majenko Technologies
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright notice, this
list of conditions and the following disclaimer in the documentation and/or
other materials provided with the distribution.
* * Neither the name of Majenko Technologies nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <WiFi.h>
#include <WiFiClient.h>
#include <WebServer.h>
#include <ESPmDNS.h>
const char *ssid = "YourSSIDHere";
const char *password = "YourPSKHere";
WebServer server(80);
const int led = 13;
void handleRoot() {
digitalWrite(led, 1);
char temp[400];
int sec = millis() / 1000;
int min = sec / 60;
int hr = min / 60;
snprintf(temp, 400,
"<html>\
<head>\
<meta http-equiv='refresh' content='5'/>\
<title>ESP32 Demo</title>\
<style>\
body { background-color: #cccccc; font-family: Arial, Helvetica, Sans-Serif; Color: #000088; }\
</style>\
</head>\
<body>\
<h1>Hello from ESP32!</h1>\
<p>Uptime: %02d:%02d:%02d</p>\
<img src=\"/test.svg\" />\
</body>\
</html>",
hr, min % 60, sec % 60
);
server.send(200, "text/html", temp);
digitalWrite(led, 0);
}
void handleNotFound() {
digitalWrite(led, 1);
String message = "File Not Found\n\n";
message += "URI: ";
message += server.uri();
message += "\nMethod: ";
message += (server.method() == HTTP_GET) ? "GET" : "POST";
message += "\nArguments: ";
message += server.args();
message += "\n";
for (uint8_t i = 0; i < server.args(); i++) {
message += " " + server.argName(i) + ": " + server.arg(i) + "\n";
}
server.send(404, "text/plain", message);
digitalWrite(led, 0);
}
void setup(void) {
pinMode(led, OUTPUT);
digitalWrite(led, 0);
Serial.begin(115200);
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
Serial.println("");
// Wait for connection
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.print("Connected to ");
Serial.println(ssid);
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
if (MDNS.begin("esp32")) {
Serial.println("MDNS responder started");
}
server.on("/", handleRoot);
server.on("/test.svg", drawGraph);
server.on("/inline", []() {
server.send(200, "text/plain", "this works as well");
});
server.onNotFound(handleNotFound);
server.begin();
Serial.println("HTTP server started");
}
void loop(void) {
server.handleClient();
delay(2);//allow the cpu to switch to other tasks
}
void drawGraph() {
String out = "";
char temp[100];
out += "<svg xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\" width=\"400\" height=\"150\">\n";
out += "<rect width=\"400\" height=\"150\" fill=\"rgb(250, 230, 210)\" stroke-width=\"1\" stroke=\"rgb(0, 0, 0)\" />\n";
out += "<g stroke=\"black\">\n";
int y = rand() % 130;
for (int x = 10; x < 390; x += 10) {
int y2 = rand() % 130;
sprintf(temp, "<line x1=\"%d\" y1=\"%d\" x2=\"%d\" y2=\"%d\" stroke-width=\"1\" />\n", x, 140 - y, x + 10, 140 - y2);
out += temp;
y = y2;
}
out += "</g>\n</svg>\n";
server.send(200, "image/svg+xml", out);
}

View File

@ -0,0 +1,304 @@
/*
FSWebServer - Example WebServer with FS backend for esp8266/esp32
Copyright (c) 2015 Hristo Gochkov. All rights reserved.
This file is part of the WebServer library for Arduino environment.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
upload the contents of the data folder with MkSPIFFS Tool ("ESP32 Sketch Data Upload" in Tools menu in Arduino IDE)
or you can upload the contents of a folder if you CD in that folder and run the following command:
for file in `ls -A1`; do curl -F "file=@$PWD/$file" esp32fs.local/edit; done
access the sample web page at http://esp32fs.local
edit the page by going to http://esp32fs.local/edit
*/
#include <WiFi.h>
#include <WiFiClient.h>
#include <WebServer.h>
#include <ESPmDNS.h>
#define FILESYSTEM SPIFFS
// You only need to format the filesystem once
#define FORMAT_FILESYSTEM false
#define DBG_OUTPUT_PORT Serial
#if FILESYSTEM == FFat
#include <FFat.h>
#endif
#if FILESYSTEM == SPIFFS
#include <SPIFFS.h>
#endif
const char* ssid = "wifi-ssid";
const char* password = "wifi-password";
const char* host = "esp32fs";
WebServer server(80);
//holds the current upload
File fsUploadFile;
//format bytes
String formatBytes(size_t bytes) {
if (bytes < 1024) {
return String(bytes) + "B";
} else if (bytes < (1024 * 1024)) {
return String(bytes / 1024.0) + "KB";
} else if (bytes < (1024 * 1024 * 1024)) {
return String(bytes / 1024.0 / 1024.0) + "MB";
} else {
return String(bytes / 1024.0 / 1024.0 / 1024.0) + "GB";
}
}
String getContentType(String filename) {
if (server.hasArg("download")) {
return "application/octet-stream";
} else if (filename.endsWith(".htm")) {
return "text/html";
} else if (filename.endsWith(".html")) {
return "text/html";
} else if (filename.endsWith(".css")) {
return "text/css";
} else if (filename.endsWith(".js")) {
return "application/javascript";
} else if (filename.endsWith(".png")) {
return "image/png";
} else if (filename.endsWith(".gif")) {
return "image/gif";
} else if (filename.endsWith(".jpg")) {
return "image/jpeg";
} else if (filename.endsWith(".ico")) {
return "image/x-icon";
} else if (filename.endsWith(".xml")) {
return "text/xml";
} else if (filename.endsWith(".pdf")) {
return "application/x-pdf";
} else if (filename.endsWith(".zip")) {
return "application/x-zip";
} else if (filename.endsWith(".gz")) {
return "application/x-gzip";
}
return "text/plain";
}
bool exists(String path){
bool yes = false;
File file = FILESYSTEM.open(path, "r");
if(!file.isDirectory()){
yes = true;
}
file.close();
return yes;
}
bool handleFileRead(String path) {
DBG_OUTPUT_PORT.println("handleFileRead: " + path);
if (path.endsWith("/")) {
path += "index.htm";
}
String contentType = getContentType(path);
String pathWithGz = path + ".gz";
if (exists(pathWithGz) || exists(path)) {
if (exists(pathWithGz)) {
path += ".gz";
}
File file = FILESYSTEM.open(path, "r");
server.streamFile(file, contentType);
file.close();
return true;
}
return false;
}
void handleFileUpload() {
if (server.uri() != "/edit") {
return;
}
HTTPUpload& upload = server.upload();
if (upload.status == UPLOAD_FILE_START) {
String filename = upload.filename;
if (!filename.startsWith("/")) {
filename = "/" + filename;
}
DBG_OUTPUT_PORT.print("handleFileUpload Name: "); DBG_OUTPUT_PORT.println(filename);
fsUploadFile = FILESYSTEM.open(filename, "w");
filename = String();
} else if (upload.status == UPLOAD_FILE_WRITE) {
//DBG_OUTPUT_PORT.print("handleFileUpload Data: "); DBG_OUTPUT_PORT.println(upload.currentSize);
if (fsUploadFile) {
fsUploadFile.write(upload.buf, upload.currentSize);
}
} else if (upload.status == UPLOAD_FILE_END) {
if (fsUploadFile) {
fsUploadFile.close();
}
DBG_OUTPUT_PORT.print("handleFileUpload Size: "); DBG_OUTPUT_PORT.println(upload.totalSize);
}
}
void handleFileDelete() {
if (server.args() == 0) {
return server.send(500, "text/plain", "BAD ARGS");
}
String path = server.arg(0);
DBG_OUTPUT_PORT.println("handleFileDelete: " + path);
if (path == "/") {
return server.send(500, "text/plain", "BAD PATH");
}
if (!exists(path)) {
return server.send(404, "text/plain", "FileNotFound");
}
FILESYSTEM.remove(path);
server.send(200, "text/plain", "");
path = String();
}
void handleFileCreate() {
if (server.args() == 0) {
return server.send(500, "text/plain", "BAD ARGS");
}
String path = server.arg(0);
DBG_OUTPUT_PORT.println("handleFileCreate: " + path);
if (path == "/") {
return server.send(500, "text/plain", "BAD PATH");
}
if (exists(path)) {
return server.send(500, "text/plain", "FILE EXISTS");
}
File file = FILESYSTEM.open(path, "w");
if (file) {
file.close();
} else {
return server.send(500, "text/plain", "CREATE FAILED");
}
server.send(200, "text/plain", "");
path = String();
}
void handleFileList() {
if (!server.hasArg("dir")) {
server.send(500, "text/plain", "BAD ARGS");
return;
}
String path = server.arg("dir");
DBG_OUTPUT_PORT.println("handleFileList: " + path);
File root = FILESYSTEM.open(path);
path = String();
String output = "[";
if(root.isDirectory()){
File file = root.openNextFile();
while(file){
if (output != "[") {
output += ',';
}
output += "{\"type\":\"";
output += (file.isDirectory()) ? "dir" : "file";
output += "\",\"name\":\"";
output += String(file.path()).substring(1);
output += "\"}";
file = root.openNextFile();
}
}
output += "]";
server.send(200, "text/json", output);
}
void setup(void) {
DBG_OUTPUT_PORT.begin(115200);
DBG_OUTPUT_PORT.print("\n");
DBG_OUTPUT_PORT.setDebugOutput(true);
if (FORMAT_FILESYSTEM) FILESYSTEM.format();
FILESYSTEM.begin();
{
File root = FILESYSTEM.open("/");
File file = root.openNextFile();
while(file){
String fileName = file.name();
size_t fileSize = file.size();
DBG_OUTPUT_PORT.printf("FS File: %s, size: %s\n", fileName.c_str(), formatBytes(fileSize).c_str());
file = root.openNextFile();
}
DBG_OUTPUT_PORT.printf("\n");
}
//WIFI INIT
DBG_OUTPUT_PORT.printf("Connecting to %s\n", ssid);
if (String(WiFi.SSID()) != String(ssid)) {
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
}
while (WiFi.status() != WL_CONNECTED) {
delay(500);
DBG_OUTPUT_PORT.print(".");
}
DBG_OUTPUT_PORT.println("");
DBG_OUTPUT_PORT.print("Connected! IP address: ");
DBG_OUTPUT_PORT.println(WiFi.localIP());
MDNS.begin(host);
DBG_OUTPUT_PORT.print("Open http://");
DBG_OUTPUT_PORT.print(host);
DBG_OUTPUT_PORT.println(".local/edit to see the file browser");
//SERVER INIT
//list directory
server.on("/list", HTTP_GET, handleFileList);
//load editor
server.on("/edit", HTTP_GET, []() {
if (!handleFileRead("/edit.htm")) {
server.send(404, "text/plain", "FileNotFound");
}
});
//create file
server.on("/edit", HTTP_PUT, handleFileCreate);
//delete file
server.on("/edit", HTTP_DELETE, handleFileDelete);
//first callback is called after the request has ended with all parsed arguments
//second callback handles file uploads at that location
server.on("/edit", HTTP_POST, []() {
server.send(200, "text/plain", "");
}, handleFileUpload);
//called when the url is not defined here
//use it to load content from FILESYSTEM
server.onNotFound([]() {
if (!handleFileRead(server.uri())) {
server.send(404, "text/plain", "FileNotFound");
}
});
//get heap status, analog input value and all GPIO statuses in one json call
server.on("/all", HTTP_GET, []() {
String json = "{";
json += "\"heap\":" + String(ESP.getFreeHeap());
json += ", \"analog\":" + String(analogRead(A0));
json += ", \"gpio\":" + String((uint32_t)(0));
json += "}";
server.send(200, "text/json", json);
json = String();
});
server.begin();
DBG_OUTPUT_PORT.println("HTTP server started");
}
void loop(void) {
server.handleClient();
delay(2);//allow the cpu to switch to other tasks
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -0,0 +1,97 @@
<!--
FSWebServer - Example Index Page
Copyright (c) 2015 Hristo Gochkov. All rights reserved.
This file is part of the WebServer library for Arduino environment.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-->
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
<title>ESP Monitor</title>
<script type="text/javascript" src="graphs.js"></script>
<script type="text/javascript">
var heap,temp,digi;
var reloadPeriod = 1000;
var running = false;
function loadValues(){
if(!running) return;
var xh = new XMLHttpRequest();
xh.onreadystatechange = function(){
if (xh.readyState == 4){
if(xh.status == 200) {
var res = JSON.parse(xh.responseText);
heap.add(res.heap);
temp.add(res.analog);
digi.add(res.gpio);
if(running) setTimeout(loadValues, reloadPeriod);
} else running = false;
}
};
xh.open("GET", "/all", true);
xh.send(null);
};
function run(){
if(!running){
running = true;
loadValues();
}
}
function onBodyLoad(){
var refreshInput = document.getElementById("refresh-rate");
refreshInput.value = reloadPeriod;
refreshInput.onchange = function(e){
var value = parseInt(e.target.value);
reloadPeriod = (value > 0)?value:0;
e.target.value = reloadPeriod;
}
var stopButton = document.getElementById("stop-button");
stopButton.onclick = function(e){
running = false;
}
var startButton = document.getElementById("start-button");
startButton.onclick = function(e){
run();
}
// Example with 10K thermistor
//function calcThermistor(v) {
// var t = Math.log(((10230000 / v) - 10000));
// t = (1/(0.001129148+(0.000234125*t)+(0.0000000876741*t*t*t)))-273.15;
// return (t>120)?0:Math.round(t*10)/10;
//}
//temp = createGraph(document.getElementById("analog"), "Temperature", 100, 128, 10, 40, false, "cyan", calcThermistor);
temp = createGraph(document.getElementById("analog"), "Analog Input", 100, 128, 0, 1023, false, "cyan");
heap = createGraph(document.getElementById("heap"), "Current Heap", 100, 125, 0, 30000, true, "orange");
digi = createDigiGraph(document.getElementById("digital"), "GPIO", 100, 146, [0, 4, 5, 16], "gold");
run();
}
</script>
</head>
<body id="index" style="margin:0; padding:0;" onload="onBodyLoad()">
<div id="controls" style="display: block; border: 1px solid rgb(68, 68, 68); padding: 5px; margin: 5px; width: 362px; background-color: rgb(238, 238, 238);">
<label>Period (ms):</label>
<input type="number" id="refresh-rate"/>
<input type="button" id="start-button" value="Start"/>
<input type="button" id="stop-button" value="Stop"/>
</div>
<div id="heap"></div>
<div id="analog"></div>
<div id="digital"></div>
</body>
</html>

View File

@ -0,0 +1,74 @@
#include <WiFi.h>
#include <WiFiClient.h>
#include <WebServer.h>
#include <ESPmDNS.h>
const char* ssid = "........";
const char* password = "........";
WebServer server(80);
const int led = 13;
void handleRoot() {
digitalWrite(led, 1);
server.send(200, "text/plain", "hello from esp32!");
digitalWrite(led, 0);
}
void handleNotFound() {
digitalWrite(led, 1);
String message = "File Not Found\n\n";
message += "URI: ";
message += server.uri();
message += "\nMethod: ";
message += (server.method() == HTTP_GET) ? "GET" : "POST";
message += "\nArguments: ";
message += server.args();
message += "\n";
for (uint8_t i = 0; i < server.args(); i++) {
message += " " + server.argName(i) + ": " + server.arg(i) + "\n";
}
server.send(404, "text/plain", message);
digitalWrite(led, 0);
}
void setup(void) {
pinMode(led, OUTPUT);
digitalWrite(led, 0);
Serial.begin(115200);
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
Serial.println("");
// Wait for connection
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.print("Connected to ");
Serial.println(ssid);
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
if (MDNS.begin("esp32")) {
Serial.println("MDNS responder started");
}
server.on("/", handleRoot);
server.on("/inline", []() {
server.send(200, "text/plain", "this works as well");
});
server.onNotFound(handleNotFound);
server.begin();
Serial.println("HTTP server started");
}
void loop(void) {
server.handleClient();
delay(2);//allow the cpu to switch to other tasks
}

View File

@ -0,0 +1,60 @@
/*
HTTP Advanced Authentication example
Created Mar 16, 2017 by Ahmed El-Sharnoby.
This example code is in the public domain.
*/
#include <WiFi.h>
#include <ESPmDNS.h>
#include <ArduinoOTA.h>
#include <WebServer.h>
const char* ssid = "........";
const char* password = "........";
WebServer server(80);
const char* www_username = "admin";
const char* www_password = "esp32";
// allows you to set the realm of authentication Default:"Login Required"
const char* www_realm = "Custom Auth Realm";
// the Content of the HTML response in case of Unautherized Access Default:empty
String authFailResponse = "Authentication Failed";
void setup() {
Serial.begin(115200);
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
if (WiFi.waitForConnectResult() != WL_CONNECTED) {
Serial.println("WiFi Connect Failed! Rebooting...");
delay(1000);
ESP.restart();
}
ArduinoOTA.begin();
server.on("/", []() {
if (!server.authenticate(www_username, www_password))
//Basic Auth Method with Custom realm and Failure Response
//return server.requestAuthentication(BASIC_AUTH, www_realm, authFailResponse);
//Digest Auth Method with realm="Login Required" and empty Failure Response
//return server.requestAuthentication(DIGEST_AUTH);
//Digest Auth Method with Custom realm and empty Failure Response
//return server.requestAuthentication(DIGEST_AUTH, www_realm);
//Digest Auth Method with Custom realm and Failure Response
{
return server.requestAuthentication(DIGEST_AUTH, www_realm, authFailResponse);
}
server.send(200, "text/plain", "Login OK");
});
server.begin();
Serial.print("Open http://");
Serial.print(WiFi.localIP());
Serial.println("/ in your browser to see it working");
}
void loop() {
ArduinoOTA.handle();
server.handleClient();
delay(2);//allow the cpu to switch to other tasks
}

View File

@ -0,0 +1,42 @@
#include <WiFi.h>
#include <ESPmDNS.h>
#include <ArduinoOTA.h>
#include <WebServer.h>
const char* ssid = "........";
const char* password = "........";
WebServer server(80);
const char* www_username = "admin";
const char* www_password = "esp32";
void setup() {
Serial.begin(115200);
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
if (WiFi.waitForConnectResult() != WL_CONNECTED) {
Serial.println("WiFi Connect Failed! Rebooting...");
delay(1000);
ESP.restart();
}
ArduinoOTA.begin();
server.on("/", []() {
if (!server.authenticate(www_username, www_password)) {
return server.requestAuthentication();
}
server.send(200, "text/plain", "Login OK");
});
server.begin();
Serial.print("Open http://");
Serial.print(WiFi.localIP());
Serial.println("/ in your browser to see it working");
}
void loop() {
ArduinoOTA.handle();
server.handleClient();
delay(2);//allow the cpu to switch to other tasks
}

View File

@ -0,0 +1,171 @@
#include <WiFi.h>
#include <WiFiClient.h>
#include <WebServer.h>
#include <ESPmDNS.h>
/*
* MultiHomedServers
*
* MultiHomedServers tests support for multi-homed servers, i.e. a distinct web servers on each IP interface.
* It only tests the case n=2 because on a basic ESP32 device, we only have two IP interfaces, namely
* the WiFi station interfaces and the WiFi soft AP interface.
*
* For this to work, the WebServer and the WiFiServer classes must correctly handle the case where an
* IP address is passed to their relevant constructor. It also requires WebServer to work with multiple,
* simultaneous instances.
*
* Testing the WebServer and the WiFiServer constructors was the primary purpose of this script.
* The part of WebServer used by this sketch does seem to work with multiple, simultaneous instances.
* However there is much functionality in WebServer that is not tested here. It may all be well, but
* that is not proven here.
*
* This sketch starts the mDNS server, as did HelloServer, and it resolves esp32.local on both interfaces,
* but was not otherwise tested.
*
* This script also tests that a server not bound to a specific IP address still works.
*
* We create three, simultaneous web servers, one specific to each interface and one that listens on both:
*
* name IP Address Port
* ---- ---------- ----
* server0 INADDR_ANY 8080
* server1 station address 8081
* server2 soft AP address 8081
*
* The expected responses to a brower's requests are as follows:
*
* 1. when client connected to the same WLAN as the station:
* Request URL Response
* ----------- --------
* http://stationaddress:8080 "hello from server0"
* http://stationaddress:8081 "hello from server1"
*
* 2. when client is connected to the soft AP:
*
* Request URL Response
* ----------- --------
* http://softAPaddress:8080 "hello from server0"
* http://softAPaddress:8081 "hello from server2"
*
* 3. Repeat 1 and 2 above with esp32.local in place of stationaddress and softAPaddress, respectively.
*
* MultiHomedServers was originally based on HelloServer.
*/
const char* ssid = "........";
const char* password = "........";
const char *apssid = "ESP32";
WebServer *server0, *server1, *server2;
const int led = 13;
void handleRoot(WebServer *server, const char *content) {
digitalWrite(led, 1);
server->send(200, "text/plain", content);
digitalWrite(led, 0);
}
void handleRoot0() {
handleRoot(server0, "hello from server0");
}
void handleRoot1() {
handleRoot(server1, "hello from server1");
}
void handleRoot2() {
handleRoot(server2, "hello from server2");
}
void handleNotFound(WebServer *server) {
digitalWrite(led, 1);
String message = "File Not Found\n\n";
message += "URI: ";
message += server->uri();
message += "\nMethod: ";
message += (server->method() == HTTP_GET) ? "GET" : "POST";
message += "\nArguments: ";
message += server->args();
message += "\n";
for (uint8_t i = 0; i < server->args(); i++) {
message += " " + server->argName(i) + ": " + server->arg(i) + "\n";
}
server->send(404, "text/plain", message);
digitalWrite(led, 0);
}
void handleNotFound0() {
handleNotFound(server0);
}
void handleNotFound1() {
handleNotFound(server1);
}
void handleNotFound2() {
handleNotFound(server2);
}
void setup(void) {
pinMode(led, OUTPUT);
digitalWrite(led, 0);
Serial.begin(115200);
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
Serial.println("");
// Wait for connection
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.print("Connected to ");
Serial.println(ssid);
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
if (!WiFi.softAP(apssid)) {
Serial.println("failed to start softAP");
for (;;) {
digitalWrite(led, 1);
delay(100);
digitalWrite(led, 0);
delay(100);
}
}
Serial.print("Soft AP: ");
Serial.print(apssid);
Serial.print(" IP address: ");
Serial.println(WiFi.softAPIP());
if (MDNS.begin("esp32")) {
Serial.println("MDNS responder started");
}
server0 = new WebServer(8080);
server1 = new WebServer(WiFi.localIP(), 8081);
server2 = new WebServer(WiFi.softAPIP(), 8081);
server0->on("/", handleRoot0);
server1->on("/", handleRoot1);
server2->on("/", handleRoot2);
server0->onNotFound(handleNotFound0);
server1->onNotFound(handleNotFound1);
server2->onNotFound(handleNotFound2);
server0->begin();
Serial.println("HTTP server0 started");
server1->begin();
Serial.println("HTTP server1 started");
server2->begin();
Serial.println("HTTP server2 started");
}
void loop(void) {
server0->handleClient();
server1->handleClient();
server2->handleClient();
delay(2);//allow the cpu to switch to other tasks
}

View File

@ -0,0 +1,57 @@
#include <WiFi.h>
#include <WiFiClient.h>
#include <WebServer.h>
#include <ESPmDNS.h>
#include <uri/UriBraces.h>
#include <uri/UriRegex.h>
const char *ssid = "........";
const char *password = "........";
WebServer server(80);
void setup(void) {
Serial.begin(9600);
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
Serial.println("");
// Wait for connection
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.print("Connected to ");
Serial.println(ssid);
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
if (MDNS.begin("esp32")) {
Serial.println("MDNS responder started");
}
server.on(F("/"), []() {
server.send(200, "text/plain", "hello from esp32!");
});
server.on(UriBraces("/users/{}"), []() {
String user = server.pathArg(0);
server.send(200, "text/plain", "User: '" + user + "'");
});
server.on(UriRegex("^\\/users\\/([0-9]+)\\/devices\\/([0-9]+)$"), []() {
String user = server.pathArg(0);
String device = server.pathArg(1);
server.send(200, "text/plain", "User: '" + user + "' and Device: '" + device + "'");
});
server.begin();
Serial.println("HTTP server started");
}
void loop(void) {
server.handleClient();
delay(2);//allow the cpu to switch to other tasks
}

View File

@ -0,0 +1,316 @@
/*
SDWebServer - Example WebServer with SD Card backend for esp8266
Copyright (c) 2015 Hristo Gochkov. All rights reserved.
This file is part of the WebServer library for Arduino environment.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
Have a FAT Formatted SD Card connected to the SPI port of the ESP8266
The web root is the SD Card root folder
File extensions with more than 3 charecters are not supported by the SD Library
File Names longer than 8 charecters will be truncated by the SD library, so keep filenames shorter
index.htm is the default index (works on subfolders as well)
upload the contents of SdRoot to the root of the SDcard and access the editor by going to http://esp8266sd.local/edit
To retrieve the contents of SDcard, visit http://esp32sd.local/list?dir=/
dir is the argument that needs to be passed to the function PrintDirectory via HTTP Get request.
*/
#include <WiFi.h>
#include <WiFiClient.h>
#include <WebServer.h>
#include <ESPmDNS.h>
#include <SPI.h>
#include <SD.h>
#define DBG_OUTPUT_PORT Serial
const char* ssid = "**********";
const char* password = "**********";
const char* host = "esp32sd";
WebServer server(80);
static bool hasSD = false;
File uploadFile;
void returnOK() {
server.send(200, "text/plain", "");
}
void returnFail(String msg) {
server.send(500, "text/plain", msg + "\r\n");
}
bool loadFromSdCard(String path) {
String dataType = "text/plain";
if (path.endsWith("/")) {
path += "index.htm";
}
if (path.endsWith(".src")) {
path = path.substring(0, path.lastIndexOf("."));
} else if (path.endsWith(".htm")) {
dataType = "text/html";
} else if (path.endsWith(".css")) {
dataType = "text/css";
} else if (path.endsWith(".js")) {
dataType = "application/javascript";
} else if (path.endsWith(".png")) {
dataType = "image/png";
} else if (path.endsWith(".gif")) {
dataType = "image/gif";
} else if (path.endsWith(".jpg")) {
dataType = "image/jpeg";
} else if (path.endsWith(".ico")) {
dataType = "image/x-icon";
} else if (path.endsWith(".xml")) {
dataType = "text/xml";
} else if (path.endsWith(".pdf")) {
dataType = "application/pdf";
} else if (path.endsWith(".zip")) {
dataType = "application/zip";
}
File dataFile = SD.open(path.c_str());
if (dataFile.isDirectory()) {
path += "/index.htm";
dataType = "text/html";
dataFile = SD.open(path.c_str());
}
if (!dataFile) {
return false;
}
if (server.hasArg("download")) {
dataType = "application/octet-stream";
}
if (server.streamFile(dataFile, dataType) != dataFile.size()) {
DBG_OUTPUT_PORT.println("Sent less data than expected!");
}
dataFile.close();
return true;
}
void handleFileUpload() {
if (server.uri() != "/edit") {
return;
}
HTTPUpload& upload = server.upload();
if (upload.status == UPLOAD_FILE_START) {
if (SD.exists((char *)upload.filename.c_str())) {
SD.remove((char *)upload.filename.c_str());
}
uploadFile = SD.open(upload.filename.c_str(), FILE_WRITE);
DBG_OUTPUT_PORT.print("Upload: START, filename: "); DBG_OUTPUT_PORT.println(upload.filename);
} else if (upload.status == UPLOAD_FILE_WRITE) {
if (uploadFile) {
uploadFile.write(upload.buf, upload.currentSize);
}
DBG_OUTPUT_PORT.print("Upload: WRITE, Bytes: "); DBG_OUTPUT_PORT.println(upload.currentSize);
} else if (upload.status == UPLOAD_FILE_END) {
if (uploadFile) {
uploadFile.close();
}
DBG_OUTPUT_PORT.print("Upload: END, Size: "); DBG_OUTPUT_PORT.println(upload.totalSize);
}
}
void deleteRecursive(String path) {
File file = SD.open((char *)path.c_str());
if (!file.isDirectory()) {
file.close();
SD.remove((char *)path.c_str());
return;
}
file.rewindDirectory();
while (true) {
File entry = file.openNextFile();
if (!entry) {
break;
}
String entryPath = path + "/" + entry.name();
if (entry.isDirectory()) {
entry.close();
deleteRecursive(entryPath);
} else {
entry.close();
SD.remove((char *)entryPath.c_str());
}
yield();
}
SD.rmdir((char *)path.c_str());
file.close();
}
void handleDelete() {
if (server.args() == 0) {
return returnFail("BAD ARGS");
}
String path = server.arg(0);
if (path == "/" || !SD.exists((char *)path.c_str())) {
returnFail("BAD PATH");
return;
}
deleteRecursive(path);
returnOK();
}
void handleCreate() {
if (server.args() == 0) {
return returnFail("BAD ARGS");
}
String path = server.arg(0);
if (path == "/" || SD.exists((char *)path.c_str())) {
returnFail("BAD PATH");
return;
}
if (path.indexOf('.') > 0) {
File file = SD.open((char *)path.c_str(), FILE_WRITE);
if (file) {
file.write(0);
file.close();
}
} else {
SD.mkdir((char *)path.c_str());
}
returnOK();
}
void printDirectory() {
if (!server.hasArg("dir")) {
return returnFail("BAD ARGS");
}
String path = server.arg("dir");
if (path != "/" && !SD.exists((char *)path.c_str())) {
return returnFail("BAD PATH");
}
File dir = SD.open((char *)path.c_str());
path = String();
if (!dir.isDirectory()) {
dir.close();
return returnFail("NOT DIR");
}
dir.rewindDirectory();
server.setContentLength(CONTENT_LENGTH_UNKNOWN);
server.send(200, "text/json", "");
WiFiClient client = server.client();
server.sendContent("[");
for (int cnt = 0; true; ++cnt) {
File entry = dir.openNextFile();
if (!entry) {
break;
}
String output;
if (cnt > 0) {
output = ',';
}
output += "{\"type\":\"";
output += (entry.isDirectory()) ? "dir" : "file";
output += "\",\"name\":\"";
output += entry.path();
output += "\"";
output += "}";
server.sendContent(output);
entry.close();
}
server.sendContent("]");
dir.close();
}
void handleNotFound() {
if (hasSD && loadFromSdCard(server.uri())) {
return;
}
String message = "SDCARD Not Detected\n\n";
message += "URI: ";
message += server.uri();
message += "\nMethod: ";
message += (server.method() == HTTP_GET) ? "GET" : "POST";
message += "\nArguments: ";
message += server.args();
message += "\n";
for (uint8_t i = 0; i < server.args(); i++) {
message += " NAME:" + server.argName(i) + "\n VALUE:" + server.arg(i) + "\n";
}
server.send(404, "text/plain", message);
DBG_OUTPUT_PORT.print(message);
}
void setup(void) {
DBG_OUTPUT_PORT.begin(115200);
DBG_OUTPUT_PORT.setDebugOutput(true);
DBG_OUTPUT_PORT.print("\n");
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
DBG_OUTPUT_PORT.print("Connecting to ");
DBG_OUTPUT_PORT.println(ssid);
// Wait for connection
uint8_t i = 0;
while (WiFi.status() != WL_CONNECTED && i++ < 20) {//wait 10 seconds
delay(500);
}
if (i == 21) {
DBG_OUTPUT_PORT.print("Could not connect to");
DBG_OUTPUT_PORT.println(ssid);
while (1) {
delay(500);
}
}
DBG_OUTPUT_PORT.print("Connected! IP address: ");
DBG_OUTPUT_PORT.println(WiFi.localIP());
if (MDNS.begin(host)) {
MDNS.addService("http", "tcp", 80);
DBG_OUTPUT_PORT.println("MDNS responder started");
DBG_OUTPUT_PORT.print("You can now connect to http://");
DBG_OUTPUT_PORT.print(host);
DBG_OUTPUT_PORT.println(".local");
}
server.on("/list", HTTP_GET, printDirectory);
server.on("/edit", HTTP_DELETE, handleDelete);
server.on("/edit", HTTP_PUT, handleCreate);
server.on("/edit", HTTP_POST, []() {
returnOK();
}, handleFileUpload);
server.onNotFound(handleNotFound);
server.begin();
DBG_OUTPUT_PORT.println("HTTP server started");
if (SD.begin(SS)) {
DBG_OUTPUT_PORT.println("SD Card initialized.");
hasSD = true;
}
}
void loop(void) {
server.handleClient();
delay(2);//allow the cpu to switch to other tasks
}

View File

@ -0,0 +1,674 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>SD Editor</title>
<style type="text/css" media="screen">
.contextMenu {
z-index: 300;
position: absolute;
left: 5px;
border: 1px solid #444;
background-color: #F5F5F5;
display: none;
box-shadow: 0 0 10px rgba( 0, 0, 0, .4 );
font-size: 12px;
font-family: sans-serif;
font-weight:bold;
}
.contextMenu ul {
list-style: none;
top: 0;
left: 0;
margin: 0;
padding: 0;
}
.contextMenu li {
position: relative;
min-width: 60px;
cursor: pointer;
}
.contextMenu span {
color: #444;
display: inline-block;
padding: 6px;
}
.contextMenu li:hover { background: #444; }
.contextMenu li:hover span { color: #EEE; }
.css-treeview ul, .css-treeview li {
padding: 0;
margin: 0;
list-style: none;
}
.css-treeview input {
position: absolute;
opacity: 0;
}
.css-treeview {
font: normal 11px Verdana, Arial, Sans-serif;
-moz-user-select: none;
-webkit-user-select: none;
user-select: none;
}
.css-treeview span {
color: #00f;
cursor: pointer;
}
.css-treeview span:hover {
text-decoration: underline;
}
.css-treeview input + label + ul {
margin: 0 0 0 22px;
}
.css-treeview input ~ ul {
display: none;
}
.css-treeview label, .css-treeview label::before {
cursor: pointer;
}
.css-treeview input:disabled + label {
cursor: default;
opacity: .6;
}
.css-treeview input:checked:not(:disabled) ~ ul {
display: block;
}
.css-treeview label, .css-treeview label::before {
background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAACgCAYAAAAFOewUAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAApxJREFUeNrslM1u00AQgGdthyalFFOK+ClIIKQKyqUVQvTEE3DmAhLwAhU8QZoH4A2Q2gMSFace4MCtJ8SPBFwAkRuiHKpA6sRN/Lu7zG5i14kctaUqRGhGXnu9O/Pt7MzsMiklvF+9t2kWTDvyIrAsA0aKRRi1T0C/hJ4LUbt5/8rNpWVlp8RSr9J40b48fxFaTQ9+ft8EZ6MJYb0Ok+dnYGpmPgXwKIAvLx8vYXc5GdMAQJgQEkpjRTh36TS2U+DWW/D17WuYgm8pwJyY1npZsZKOxImOV1I/h4+O6vEg5GCZBpgmA6hX8wHKUHDRBXQYicQ4rlc3Tf0VMs8DHBS864F2YFspjgUYjKX/Az3gsdQd2eeBHwmdGWXHcgBGSkZXOXohcEXebRoQcAgjqediNY+AVyu3Z3sAKqfKoGMsewBeEIOPgQxxPJIjcGH6qtL/0AdADzKGnuuD+2tLK7Q8DhHHbOBW+KEzcHLuYc82MkEUekLiwuvVH+guQBQzOG4XdAb8EOcRcqQvDkY2iCLuxECJ43JobMXoutqGgDa2T7UqLKwt9KRyuxKVByqVXXqIoCCUCAqhUOioTWC7G4TQEOD0APy2/7G2Xpu1J4+lxeQ4TXBbITDpoVelRN/BVFbwu5oMMJUBhoXy5tmdRcMwymP2OLQaLjx9/vnBo6V3K6izATmSnMa0Dq7ferIohJhr1p01zrlz49rZF4OMs8JkX23vVQzYp+wbYGV/KpXKjvspl8tsIKCrMNAYFxj2GKS5ZWxg4ewKsJfaGMIY5KXqPz8LBBj6+yDvVP79+yDp/9F9oIx3OisHWwe7Oal0HxCAAAQgAAEIQAACEIAABCAAAQhAAAIQgAAEIAABCEAAAhCAAAQgwD8E/BZgAP0qhKj3rXO7AAAAAElFTkSuQmCC") no-repeat;
}
.css-treeview label, .css-treeview span, .css-treeview label::before {
display: inline-block;
height: 16px;
line-height: 16px;
vertical-align: middle;
}
.css-treeview label {
background-position: 18px 0;
}
.css-treeview label::before {
content: "";
width: 16px;
margin: 0 22px 0 0;
vertical-align: middle;
background-position: 0 -32px;
}
.css-treeview input:checked + label::before {
background-position: 0 -16px;
}
/* webkit adjacent element selector bugfix */
@media screen and (-webkit-min-device-pixel-ratio:0)
{
.css-treeview{
-webkit-animation: webkit-adjacent-element-selector-bugfix infinite 1s;
}
@-webkit-keyframes webkit-adjacent-element-selector-bugfix
{
from {
padding: 0;
}
to {
padding: 0;
}
}
}
#uploader {
position: absolute;
top: 0;
right: 0;
left: 0;
height:28px;
line-height: 24px;
padding-left: 10px;
background-color: #444;
color:#EEE;
}
#tree {
position: absolute;
top: 28px;
bottom: 0;
left: 0;
width:200px;
padding: 8px;
}
#editor, #preview {
position: absolute;
top: 28px;
right: 0;
bottom: 0;
left: 200px;
}
#preview {
background-color: #EEE;
padding:5px;
}
</style>
<script>
function createFileUploader(element, tree, editor){
var xmlHttp;
var input = document.createElement("input");
input.type = "file";
input.multiple = false;
input.name = "data";
document.getElementById(element).appendChild(input);
var path = document.createElement("input");
path.id = "upload-path";
path.type = "text";
path.name = "path";
path.defaultValue = "/";
document.getElementById(element).appendChild(path);
var button = document.createElement("button");
button.innerHTML = 'Upload';
document.getElementById(element).appendChild(button);
var mkdir = document.createElement("button");
mkdir.innerHTML = 'MkDir';
document.getElementById(element).appendChild(mkdir);
var mkfile = document.createElement("button");
mkfile.innerHTML = 'MkFile';
document.getElementById(element).appendChild(mkfile);
function httpPostProcessRequest(){
if (xmlHttp.readyState == 4){
if(xmlHttp.status != 200) alert("ERROR["+xmlHttp.status+"]: "+xmlHttp.responseText);
else {
tree.refreshPath(path.value);
}
}
}
function createPath(p){
xmlHttp = new XMLHttpRequest();
xmlHttp.onreadystatechange = httpPostProcessRequest;
var formData = new FormData();
formData.append("path", p);
xmlHttp.open("PUT", "/edit");
xmlHttp.send(formData);
}
mkfile.onclick = function(e){
if(path.value.indexOf(".") === -1) return;
createPath(path.value);
editor.loadUrl(path.value);
};
mkdir.onclick = function(e){
if(path.value.length < 2) return;
var dir = path.value
if(dir.indexOf(".") !== -1){
if(dir.lastIndexOf("/") === 0) return;
dir = dir.substring(0, dir.lastIndexOf("/"));
}
createPath(dir);
};
button.onclick = function(e){
if(input.files.length === 0){
return;
}
xmlHttp = new XMLHttpRequest();
xmlHttp.onreadystatechange = httpPostProcessRequest;
var formData = new FormData();
formData.append("data", input.files[0], path.value);
xmlHttp.open("POST", "/edit");
xmlHttp.send(formData);
}
input.onchange = function(e){
if(input.files.length === 0) return;
var filename = input.files[0].name;
var ext = /(?:\.([^.]+))?$/.exec(filename)[1];
var name = /(.*)\.[^.]+$/.exec(filename)[1];
if(typeof name !== undefined){
if(name.length > 8) name = name.substring(0, 8);
filename = name;
}
if(typeof ext !== undefined){
if(ext === "html") ext = "htm";
else if(ext === "jpeg") ext = "jpg";
filename = filename + "." + ext;
}
if(path.value === "/" || path.value.lastIndexOf("/") === 0){
path.value = "/"+filename;
} else {
path.value = path.value.substring(0, path.value.lastIndexOf("/")+1)+filename;
}
}
}
function createTree(element, editor){
var preview = document.getElementById("preview");
var treeRoot = document.createElement("div");
treeRoot.className = "css-treeview";
document.getElementById(element).appendChild(treeRoot);
function loadDownload(path){
document.getElementById('download-frame').src = path+"?download=true";
}
function loadPreview(path){
document.getElementById("editor").style.display = "none";
preview.style.display = "block";
preview.innerHTML = '<img src="'+path+'" style="max-width:100%; max-height:100%; margin:auto; display:block;" />';
}
function fillFolderMenu(el, path){
var list = document.createElement("ul");
el.appendChild(list);
var action = document.createElement("li");
list.appendChild(action);
var isChecked = document.getElementById(path).checked;
var expnd = document.createElement("li");
list.appendChild(expnd);
if(isChecked){
expnd.innerHTML = "<span>Collapse</span>";
expnd.onclick = function(e){
document.getElementById(path).checked = false;
if(document.body.getElementsByClassName('contextMenu').length > 0) document.body.removeChild(el);
};
var refrsh = document.createElement("li");
list.appendChild(refrsh);
refrsh.innerHTML = "<span>Refresh</span>";
refrsh.onclick = function(e){
var leaf = document.getElementById(path).parentNode;
if(leaf.childNodes.length == 3) leaf.removeChild(leaf.childNodes[2]);
httpGet(leaf, path);
if(document.body.getElementsByClassName('contextMenu').length > 0) document.body.removeChild(el);
};
} else {
expnd.innerHTML = "<span>Expand</span>";
expnd.onclick = function(e){
document.getElementById(path).checked = true;
var leaf = document.getElementById(path).parentNode;
if(leaf.childNodes.length == 3) leaf.removeChild(leaf.childNodes[2]);
httpGet(leaf, path);
if(document.body.getElementsByClassName('contextMenu').length > 0) document.body.removeChild(el);
};
}
var upload = document.createElement("li");
list.appendChild(upload);
upload.innerHTML = "<span>Upload</span>";
upload.onclick = function(e){
var pathEl = document.getElementById("upload-path");
if(pathEl){
var subPath = pathEl.value;
if(subPath.lastIndexOf("/") < 1) pathEl.value = path+subPath;
else pathEl.value = path.substring(subPath.lastIndexOf("/"))+subPath;
}
if(document.body.getElementsByClassName('contextMenu').length > 0) document.body.removeChild(el);
};
var delFile = document.createElement("li");
list.appendChild(delFile);
delFile.innerHTML = "<span>Delete</span>";
delFile.onclick = function(e){
httpDelete(path);
if(document.body.getElementsByClassName('contextMenu').length > 0) document.body.removeChild(el);
};
}
function fillFileMenu(el, path){
var list = document.createElement("ul");
el.appendChild(list);
var action = document.createElement("li");
list.appendChild(action);
if(isTextFile(path)){
action.innerHTML = "<span>Edit</span>";
action.onclick = function(e){
editor.loadUrl(path);
if(document.body.getElementsByClassName('contextMenu').length > 0) document.body.removeChild(el);
};
} else if(isImageFile(path)){
action.innerHTML = "<span>Preview</span>";
action.onclick = function(e){
loadPreview(path);
if(document.body.getElementsByClassName('contextMenu').length > 0) document.body.removeChild(el);
};
}
var download = document.createElement("li");
list.appendChild(download);
download.innerHTML = "<span>Download</span>";
download.onclick = function(e){
loadDownload(path);
if(document.body.getElementsByClassName('contextMenu').length > 0) document.body.removeChild(el);
};
var delFile = document.createElement("li");
list.appendChild(delFile);
delFile.innerHTML = "<span>Delete</span>";
delFile.onclick = function(e){
httpDelete(path);
if(document.body.getElementsByClassName('contextMenu').length > 0) document.body.removeChild(el);
};
}
function showContextMenu(e, path, isfile){
var divContext = document.createElement("div");
var scrollTop = document.body.scrollTop ? document.body.scrollTop : document.documentElement.scrollTop;
var scrollLeft = document.body.scrollLeft ? document.body.scrollLeft : document.documentElement.scrollLeft;
var left = e.clientX + scrollLeft;
var top = e.clientY + scrollTop;
divContext.className = 'contextMenu';
divContext.style.display = 'block';
divContext.style.left = left + 'px';
divContext.style.top = top + 'px';
if(isfile) fillFileMenu(divContext, path);
else fillFolderMenu(divContext, path);
document.body.appendChild(divContext);
var width = divContext.offsetWidth;
var height = divContext.offsetHeight;
divContext.onmouseout = function(e){
if(e.clientX < left || e.clientX > (left + width) || e.clientY < top || e.clientY > (top + height)){
if(document.body.getElementsByClassName('contextMenu').length > 0) document.body.removeChild(divContext);
}
};
}
function createTreeLeaf(path, name, size){
var leaf = document.createElement("li");
leaf.id = name.toLowerCase();
var label = document.createElement("span");
label.textContent = name.toLowerCase();
leaf.appendChild(label);
leaf.onclick = function(e){
if(isTextFile(leaf.id)){
editor.loadUrl(leaf.id);
} else if(isImageFile(leaf.id)){
loadPreview(leaf.id);
}
};
leaf.oncontextmenu = function(e){
e.preventDefault();
e.stopPropagation();
showContextMenu(e, leaf.id, true);
};
return leaf;
}
function createTreeBranch(path, name, disabled){
var leaf = document.createElement("li");
var check = document.createElement("input");
check.type = "checkbox";
check.id = name.toLowerCase();
if(typeof disabled !== "undefined" && disabled) check.disabled = "disabled";
leaf.appendChild(check);
var label = document.createElement("label");
label.for = check.id;
label.textContent = name.toLowerCase();
leaf.appendChild(label);
check.onchange = function(e){
if(check.checked){
if(leaf.childNodes.length == 3) leaf.removeChild(leaf.childNodes[2]);
httpGet(leaf, check.id);
}
};
label.onclick = function(e){
if(!check.checked){
check.checked = true;
if(leaf.childNodes.length == 3) leaf.removeChild(leaf.childNodes[2]);
httpGet(leaf, check.id);
} else {
check.checked = false;
}
};
leaf.oncontextmenu = function(e){
e.preventDefault();
e.stopPropagation();
showContextMenu(e, check.id, false);
}
return leaf;
}
function addList(parent, path, items){
var list = document.createElement("ul");
parent.appendChild(list);
var ll = items.length;
for(var i = 0; i < ll; i++){
var item = items[i];
var itemEl;
if(item.type === "file"){
itemEl = createTreeLeaf(path, item.name, item.size);
} else {
itemEl = createTreeBranch(path, item.name);
}
list.appendChild(itemEl);
}
}
function isTextFile(path){
var ext = /(?:\.([^.]+))?$/.exec(path)[1];
if(typeof ext !== undefined){
switch(ext){
case "txt":
case "htm":
case "html":
case "js":
case "json":
case "c":
case "h":
case "cpp":
case "css":
case "xml":
return true;
}
}
return false;
}
function isImageFile(path){
var ext = /(?:\.([^.]+))?$/.exec(path)[1];
if(typeof ext !== undefined){
switch(ext){
case "png":
case "jpg":
case "gif":
case "ico":
return true;
}
}
return false;
}
this.refreshPath = function(path){
if(path.lastIndexOf('/') < 1){
path = '/';
treeRoot.removeChild(treeRoot.childNodes[0]);
httpGet(treeRoot, "/");
} else {
path = path.substring(0, path.lastIndexOf('/'));
var leaf = document.getElementById(path).parentNode;
if(leaf.childNodes.length == 3) leaf.removeChild(leaf.childNodes[2]);
httpGet(leaf, path);
}
};
function delCb(path){
return function(){
if (xmlHttp.readyState == 4){
if(xmlHttp.status != 200){
alert("ERROR["+xmlHttp.status+"]: "+xmlHttp.responseText);
} else {
if(path.lastIndexOf('/') < 1){
path = '/';
treeRoot.removeChild(treeRoot.childNodes[0]);
httpGet(treeRoot, "/");
} else {
path = path.substring(0, path.lastIndexOf('/'));
var leaf = document.getElementById(path).parentNode;
if(leaf.childNodes.length == 3) leaf.removeChild(leaf.childNodes[2]);
httpGet(leaf, path);
}
}
}
}
}
function httpDelete(filename){
xmlHttp = new XMLHttpRequest();
xmlHttp.onreadystatechange = delCb(filename);
var formData = new FormData();
formData.append("path", filename);
xmlHttp.open("DELETE", "/edit");
xmlHttp.send(formData);
}
function getCb(parent, path){
return function(){
if (xmlHttp.readyState == 4){
//clear loading
if(xmlHttp.status == 200) addList(parent, path, JSON.parse(xmlHttp.responseText));
}
}
}
function httpGet(parent, path){
xmlHttp = new XMLHttpRequest(parent, path);
xmlHttp.onreadystatechange = getCb(parent, path);
xmlHttp.open("GET", "/list?dir="+path, true);
xmlHttp.send(null);
//start loading
}
httpGet(treeRoot, "/");
return this;
}
function createEditor(element, file, lang, theme, type){
function getLangFromFilename(filename){
var lang = "plain";
var ext = /(?:\.([^.]+))?$/.exec(filename)[1];
if(typeof ext !== undefined){
switch(ext){
case "txt": lang = "plain"; break;
case "htm": lang = "html"; break;
case "js": lang = "javascript"; break;
case "c": lang = "c_cpp"; break;
case "cpp": lang = "c_cpp"; break;
case "css":
case "scss":
case "php":
case "html":
case "json":
case "xml":
lang = ext;
}
}
return lang;
}
if(typeof file === "undefined") file = "/index.htm";
if(typeof lang === "undefined"){
lang = getLangFromFilename(file);
}
if(typeof theme === "undefined") theme = "textmate";
if(typeof type === "undefined"){
type = "text/"+lang;
if(lang === "c_cpp") type = "text/plain";
}
var xmlHttp = null;
var editor = ace.edit(element);
//post
function httpPostProcessRequest(){
if (xmlHttp.readyState == 4){
if(xmlHttp.status != 200) alert("ERROR["+xmlHttp.status+"]: "+xmlHttp.responseText);
}
}
function httpPost(filename, data, type){
xmlHttp = new XMLHttpRequest();
xmlHttp.onreadystatechange = httpPostProcessRequest;
var formData = new FormData();
formData.append("data", new Blob([data], { type: type }), filename);
xmlHttp.open("POST", "/edit");
xmlHttp.send(formData);
}
//get
function httpGetProcessRequest(){
if (xmlHttp.readyState == 4){
document.getElementById("preview").style.display = "none";
document.getElementById("editor").style.display = "block";
if(xmlHttp.status == 200) editor.setValue(xmlHttp.responseText);
else editor.setValue("");
editor.clearSelection();
}
}
function httpGet(theUrl){
xmlHttp = new XMLHttpRequest();
xmlHttp.onreadystatechange = httpGetProcessRequest;
xmlHttp.open("GET", theUrl, true);
xmlHttp.send(null);
}
if(lang !== "plain") editor.getSession().setMode("ace/mode/"+lang);
editor.setTheme("ace/theme/"+theme);
editor.$blockScrolling = Infinity;
editor.getSession().setUseSoftTabs(true);
editor.getSession().setTabSize(2);
editor.setHighlightActiveLine(true);
editor.setShowPrintMargin(false);
editor.commands.addCommand({
name: 'saveCommand',
bindKey: {win: 'Ctrl-S', mac: 'Command-S'},
exec: function(editor) {
httpPost(file, editor.getValue()+"", type);
},
readOnly: false
});
editor.commands.addCommand({
name: 'undoCommand',
bindKey: {win: 'Ctrl-Z', mac: 'Command-Z'},
exec: function(editor) {
editor.getSession().getUndoManager().undo(false);
},
readOnly: false
});
editor.commands.addCommand({
name: 'redoCommand',
bindKey: {win: 'Ctrl-Shift-Z', mac: 'Command-Shift-Z'},
exec: function(editor) {
editor.getSession().getUndoManager().redo(false);
},
readOnly: false
});
httpGet(file);
editor.loadUrl = function(filename){
file = filename;
lang = getLangFromFilename(file);
type = "text/"+lang;
if(lang !== "plain") editor.getSession().setMode("ace/mode/"+lang);
httpGet(file);
}
return editor;
}
function onBodyLoad(){
var vars = {};
var parts = window.location.href.replace(/[?&]+([^=&]+)=([^&]*)/gi, function(m,key,value) { vars[key] = value; });
var editor = createEditor("editor", vars.file, vars.lang, vars.theme);
var tree = createTree("tree", editor);
createFileUploader("uploader", tree, editor);
};
</script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.1.9/ace.js" type="text/javascript" charset="utf-8"></script>
</head>
<body onload="onBodyLoad();">
<div id="uploader"></div>
<div id="tree"></div>
<div id="editor"></div>
<div id="preview" style="display:none;"></div>
<iframe id=download-frame style='display:none;'></iframe>
</body>
</html>

View File

@ -0,0 +1,22 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
<title>ESP Index</title>
<style>
body {
background-color:black;
color:white;
}
</style>
<script type="text/javascript">
function onBodyLoad(){
console.log("we are loaded!!");
}
</script>
</head>
<body id="index" onload="onBodyLoad()">
<h1>ESP8266 Pin Functions</h1>
<img src="pins.png" />
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 174 KiB

View File

@ -0,0 +1,133 @@
#include <WiFi.h>
#include <WiFiClient.h>
#include <WebServer.h>
const char* ssid = "........";
const char* password = "........";
WebServer server(80);
//Check if header is present and correct
bool is_authentified() {
Serial.println("Enter is_authentified");
if (server.hasHeader("Cookie")) {
Serial.print("Found cookie: ");
String cookie = server.header("Cookie");
Serial.println(cookie);
if (cookie.indexOf("ESPSESSIONID=1") != -1) {
Serial.println("Authentification Successful");
return true;
}
}
Serial.println("Authentification Failed");
return false;
}
//login page, also called for disconnect
void handleLogin() {
String msg;
if (server.hasHeader("Cookie")) {
Serial.print("Found cookie: ");
String cookie = server.header("Cookie");
Serial.println(cookie);
}
if (server.hasArg("DISCONNECT")) {
Serial.println("Disconnection");
server.sendHeader("Location", "/login");
server.sendHeader("Cache-Control", "no-cache");
server.sendHeader("Set-Cookie", "ESPSESSIONID=0");
server.send(301);
return;
}
if (server.hasArg("USERNAME") && server.hasArg("PASSWORD")) {
if (server.arg("USERNAME") == "admin" && server.arg("PASSWORD") == "admin") {
server.sendHeader("Location", "/");
server.sendHeader("Cache-Control", "no-cache");
server.sendHeader("Set-Cookie", "ESPSESSIONID=1");
server.send(301);
Serial.println("Log in Successful");
return;
}
msg = "Wrong username/password! try again.";
Serial.println("Log in Failed");
}
String content = "<html><body><form action='/login' method='POST'>To log in, please use : admin/admin<br>";
content += "User:<input type='text' name='USERNAME' placeholder='user name'><br>";
content += "Password:<input type='password' name='PASSWORD' placeholder='password'><br>";
content += "<input type='submit' name='SUBMIT' value='Submit'></form>" + msg + "<br>";
content += "You also can go <a href='/inline'>here</a></body></html>";
server.send(200, "text/html", content);
}
//root page can be accessed only if authentification is ok
void handleRoot() {
Serial.println("Enter handleRoot");
String header;
if (!is_authentified()) {
server.sendHeader("Location", "/login");
server.sendHeader("Cache-Control", "no-cache");
server.send(301);
return;
}
String content = "<html><body><H2>hello, you successfully connected to esp8266!</H2><br>";
if (server.hasHeader("User-Agent")) {
content += "the user agent used is : " + server.header("User-Agent") + "<br><br>";
}
content += "You can access this page until you <a href=\"/login?DISCONNECT=YES\">disconnect</a></body></html>";
server.send(200, "text/html", content);
}
//no need authentification
void handleNotFound() {
String message = "File Not Found\n\n";
message += "URI: ";
message += server.uri();
message += "\nMethod: ";
message += (server.method() == HTTP_GET) ? "GET" : "POST";
message += "\nArguments: ";
message += server.args();
message += "\n";
for (uint8_t i = 0; i < server.args(); i++) {
message += " " + server.argName(i) + ": " + server.arg(i) + "\n";
}
server.send(404, "text/plain", message);
}
void setup(void) {
Serial.begin(115200);
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
Serial.println("");
// Wait for connection
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.print("Connected to ");
Serial.println(ssid);
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
server.on("/", handleRoot);
server.on("/login", handleLogin);
server.on("/inline", []() {
server.send(200, "text/plain", "this works without need of authentification");
});
server.onNotFound(handleNotFound);
//here the list of headers to be recorded
const char * headerkeys[] = {"User-Agent", "Cookie"} ;
size_t headerkeyssize = sizeof(headerkeys) / sizeof(char*);
//ask server to track these headers
server.collectHeaders(headerkeys, headerkeyssize);
server.begin();
Serial.println("HTTP server started");
}
void loop(void) {
server.handleClient();
delay(2);//allow the cpu to switch to other tasks
}

View File

@ -0,0 +1,69 @@
/*
To upload through terminal you can use: curl -F "image=@firmware.bin" esp8266-webupdate.local/update
*/
#include <WiFi.h>
#include <WiFiClient.h>
#include <WebServer.h>
#include <ESPmDNS.h>
#include <Update.h>
const char* host = "esp32-webupdate";
const char* ssid = "........";
const char* password = "........";
WebServer server(80);
const char* serverIndex = "<form method='POST' action='/update' enctype='multipart/form-data'><input type='file' name='update'><input type='submit' value='Update'></form>";
void setup(void) {
Serial.begin(115200);
Serial.println();
Serial.println("Booting Sketch...");
WiFi.mode(WIFI_AP_STA);
WiFi.begin(ssid, password);
if (WiFi.waitForConnectResult() == WL_CONNECTED) {
MDNS.begin(host);
server.on("/", HTTP_GET, []() {
server.sendHeader("Connection", "close");
server.send(200, "text/html", serverIndex);
});
server.on("/update", HTTP_POST, []() {
server.sendHeader("Connection", "close");
server.send(200, "text/plain", (Update.hasError()) ? "FAIL" : "OK");
ESP.restart();
}, []() {
HTTPUpload& upload = server.upload();
if (upload.status == UPLOAD_FILE_START) {
Serial.setDebugOutput(true);
Serial.printf("Update: %s\n", upload.filename.c_str());
if (!Update.begin()) { //start with max available size
Update.printError(Serial);
}
} else if (upload.status == UPLOAD_FILE_WRITE) {
if (Update.write(upload.buf, upload.currentSize) != upload.currentSize) {
Update.printError(Serial);
}
} else if (upload.status == UPLOAD_FILE_END) {
if (Update.end(true)) { //true to set the size to the current progress
Serial.printf("Update Success: %u\nRebooting...\n", upload.totalSize);
} else {
Update.printError(Serial);
}
Serial.setDebugOutput(false);
} else {
Serial.printf("Update Failed Unexpectedly (likely broken connection): status=%d\n", upload.status);
}
});
server.begin();
MDNS.addService("http", "tcp", 80);
Serial.printf("Ready! Open http://%s.local in your browser\n", host);
} else {
Serial.println("WiFi Failed");
}
}
void loop(void) {
server.handleClient();
delay(2);//allow the cpu to switch to other tasks
}

View File

@ -0,0 +1,38 @@
#######################################
# Syntax Coloring Map For Ultrasound
#######################################
#######################################
# Datatypes (KEYWORD1)
#######################################
WebServer KEYWORD1
WebServerSecure KEYWORD1
HTTPMethod KEYWORD1
#######################################
# Methods and Functions (KEYWORD2)
#######################################
begin KEYWORD2
handleClient KEYWORD2
on KEYWORD2
addHandler KEYWORD2
uri KEYWORD2
method KEYWORD2
client KEYWORD2
send KEYWORD2
arg KEYWORD2
argName KEYWORD2
args KEYWORD2
hasArg KEYWORD2
onNotFound KEYWORD2
#######################################
# Constants (LITERAL1)
#######################################
HTTP_GET LITERAL1
HTTP_POST LITERAL1
HTTP_ANY LITERAL1
CONTENT_LENGTH_UNKNOWN LITERAL1

View File

@ -0,0 +1,9 @@
name=WebServer
version=2.0.0
author=Ivan Grokhotkov
maintainer=Ivan Grokhtkov <ivan@esp8266.com>
sentence=Simple web server library
paragraph=The library supports HTTP GET and POST requests, provides argument parsing, handles one client at a time.
category=Communication
url=
architectures=esp32

View File

@ -0,0 +1,9 @@
#ifndef _HTTP_Method_H_
#define _HTTP_Method_H_
#include "http_parser.h"
typedef enum http_method HTTPMethod;
#define HTTP_ANY (HTTPMethod)(255)
#endif /* _HTTP_Method_H_ */

View File

@ -0,0 +1,588 @@
/*
Parsing.cpp - HTTP request parsing.
Copyright (c) 2015 Ivan Grokhotkov. All rights reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
Modified 8 May 2015 by Hristo Gochkov (proper post and file upload handling)
*/
#include <Arduino.h>
#include <esp32-hal-log.h>
#include "WiFiServer.h"
#include "WiFiClient.h"
#include "WebServer.h"
#include "detail/mimetable.h"
#ifndef WEBSERVER_MAX_POST_ARGS
#define WEBSERVER_MAX_POST_ARGS 32
#endif
#define __STR(a) #a
#define _STR(a) __STR(a)
const char * _http_method_str[] = {
#define XX(num, name, string) _STR(name),
HTTP_METHOD_MAP(XX)
#undef XX
};
static const char Content_Type[] PROGMEM = "Content-Type";
static const char filename[] PROGMEM = "filename";
static char* readBytesWithTimeout(WiFiClient& client, size_t maxLength, size_t& dataLength, int timeout_ms)
{
char *buf = nullptr;
dataLength = 0;
while (dataLength < maxLength) {
int tries = timeout_ms;
size_t newLength;
while (!(newLength = client.available()) && tries--) delay(1);
if (!newLength) {
break;
}
if (!buf) {
buf = (char *) malloc(newLength + 1);
if (!buf) {
return nullptr;
}
}
else {
char* newBuf = (char *) realloc(buf, dataLength + newLength + 1);
if (!newBuf) {
free(buf);
return nullptr;
}
buf = newBuf;
}
client.readBytes(buf + dataLength, newLength);
dataLength += newLength;
buf[dataLength] = '\0';
}
return buf;
}
bool WebServer::_parseRequest(WiFiClient& client) {
// Read the first line of HTTP request
String req = client.readStringUntil('\r');
client.readStringUntil('\n');
//reset header value
for (int i = 0; i < _headerKeysCount; ++i) {
_currentHeaders[i].value =String();
}
// First line of HTTP request looks like "GET /path HTTP/1.1"
// Retrieve the "/path" part by finding the spaces
int addr_start = req.indexOf(' ');
int addr_end = req.indexOf(' ', addr_start + 1);
if (addr_start == -1 || addr_end == -1) {
log_e("Invalid request: %s", req.c_str());
return false;
}
String methodStr = req.substring(0, addr_start);
String url = req.substring(addr_start + 1, addr_end);
String versionEnd = req.substring(addr_end + 8);
_currentVersion = atoi(versionEnd.c_str());
String searchStr = "";
int hasSearch = url.indexOf('?');
if (hasSearch != -1){
searchStr = url.substring(hasSearch + 1);
url = url.substring(0, hasSearch);
}
_currentUri = url;
_chunked = false;
_clientContentLength = 0; // not known yet, or invalid
HTTPMethod method = HTTP_ANY;
size_t num_methods = sizeof(_http_method_str) / sizeof(const char *);
for (size_t i=0; i<num_methods; i++) {
if (methodStr == _http_method_str[i]) {
method = (HTTPMethod)i;
break;
}
}
if (method == HTTP_ANY) {
log_e("Unknown HTTP Method: %s", methodStr.c_str());
return false;
}
_currentMethod = method;
log_v("method: %s url: %s search: %s", methodStr.c_str(), url.c_str(), searchStr.c_str());
//attach handler
RequestHandler* handler;
for (handler = _firstHandler; handler; handler = handler->next()) {
if (handler->canHandle(_currentMethod, _currentUri))
break;
}
_currentHandler = handler;
String formData;
// below is needed only when POST type request
if (method == HTTP_POST || method == HTTP_PUT || method == HTTP_PATCH || method == HTTP_DELETE){
String boundaryStr;
String headerName;
String headerValue;
bool isForm = false;
bool isEncoded = false;
//parse headers
while(1){
req = client.readStringUntil('\r');
client.readStringUntil('\n');
if (req == "") break;//no moar headers
int headerDiv = req.indexOf(':');
if (headerDiv == -1){
break;
}
headerName = req.substring(0, headerDiv);
headerValue = req.substring(headerDiv + 1);
headerValue.trim();
_collectHeader(headerName.c_str(),headerValue.c_str());
log_v("headerName: %s", headerName.c_str());
log_v("headerValue: %s", headerValue.c_str());
if (headerName.equalsIgnoreCase(FPSTR(Content_Type))){
using namespace mime;
if (headerValue.startsWith(FPSTR(mimeTable[txt].mimeType))){
isForm = false;
} else if (headerValue.startsWith(F("application/x-www-form-urlencoded"))){
isForm = false;
isEncoded = true;
} else if (headerValue.startsWith(F("multipart/"))){
boundaryStr = headerValue.substring(headerValue.indexOf('=') + 1);
boundaryStr.replace("\"","");
isForm = true;
}
} else if (headerName.equalsIgnoreCase(F("Content-Length"))){
_clientContentLength = headerValue.toInt();
} else if (headerName.equalsIgnoreCase(F("Host"))){
_hostHeader = headerValue;
}
}
if (!isForm){
size_t plainLength;
char* plainBuf = readBytesWithTimeout(client, _clientContentLength, plainLength, HTTP_MAX_POST_WAIT);
if (plainLength < _clientContentLength) {
free(plainBuf);
return false;
}
if (_clientContentLength > 0) {
if(isEncoded){
//url encoded form
if (searchStr != "") searchStr += '&';
searchStr += plainBuf;
}
_parseArguments(searchStr);
if(!isEncoded){
//plain post json or other data
RequestArgument& arg = _currentArgs[_currentArgCount++];
arg.key = F("plain");
arg.value = String(plainBuf);
}
log_v("Plain: %s", plainBuf);
free(plainBuf);
} else {
// No content - but we can still have arguments in the URL.
_parseArguments(searchStr);
}
} else {
// it IS a form
_parseArguments(searchStr);
if (!_parseForm(client, boundaryStr, _clientContentLength)) {
return false;
}
}
} else {
String headerName;
String headerValue;
//parse headers
while(1){
req = client.readStringUntil('\r');
client.readStringUntil('\n');
if (req == "") break;//no moar headers
int headerDiv = req.indexOf(':');
if (headerDiv == -1){
break;
}
headerName = req.substring(0, headerDiv);
headerValue = req.substring(headerDiv + 2);
_collectHeader(headerName.c_str(),headerValue.c_str());
log_v("headerName: %s", headerName.c_str());
log_v("headerValue: %s", headerValue.c_str());
if (headerName.equalsIgnoreCase("Host")){
_hostHeader = headerValue;
}
}
_parseArguments(searchStr);
}
client.flush();
log_v("Request: %s", url.c_str());
log_v(" Arguments: %s", searchStr.c_str());
return true;
}
bool WebServer::_collectHeader(const char* headerName, const char* headerValue) {
for (int i = 0; i < _headerKeysCount; i++) {
if (_currentHeaders[i].key.equalsIgnoreCase(headerName)) {
_currentHeaders[i].value=headerValue;
return true;
}
}
return false;
}
void WebServer::_parseArguments(String data) {
log_v("args: %s", data.c_str());
if (_currentArgs)
delete[] _currentArgs;
_currentArgs = 0;
if (data.length() == 0) {
_currentArgCount = 0;
_currentArgs = new RequestArgument[1];
return;
}
_currentArgCount = 1;
for (int i = 0; i < (int)data.length(); ) {
i = data.indexOf('&', i);
if (i == -1)
break;
++i;
++_currentArgCount;
}
log_v("args count: %d", _currentArgCount);
_currentArgs = new RequestArgument[_currentArgCount+1];
int pos = 0;
int iarg;
for (iarg = 0; iarg < _currentArgCount;) {
int equal_sign_index = data.indexOf('=', pos);
int next_arg_index = data.indexOf('&', pos);
log_v("pos %d =@%d &@%d", pos, equal_sign_index, next_arg_index);
if ((equal_sign_index == -1) || ((equal_sign_index > next_arg_index) && (next_arg_index != -1))) {
log_e("arg missing value: %d", iarg);
if (next_arg_index == -1)
break;
pos = next_arg_index + 1;
continue;
}
RequestArgument& arg = _currentArgs[iarg];
arg.key = urlDecode(data.substring(pos, equal_sign_index));
arg.value = urlDecode(data.substring(equal_sign_index + 1, next_arg_index));
log_v("arg %d key: %s value: %s", iarg, arg.key.c_str(), arg.value.c_str());
++iarg;
if (next_arg_index == -1)
break;
pos = next_arg_index + 1;
}
_currentArgCount = iarg;
log_v("args count: %d", _currentArgCount);
}
void WebServer::_uploadWriteByte(uint8_t b){
if (_currentUpload->currentSize == HTTP_UPLOAD_BUFLEN){
if(_currentHandler && _currentHandler->canUpload(_currentUri))
_currentHandler->upload(*this, _currentUri, *_currentUpload);
_currentUpload->totalSize += _currentUpload->currentSize;
_currentUpload->currentSize = 0;
}
_currentUpload->buf[_currentUpload->currentSize++] = b;
}
int WebServer::_uploadReadByte(WiFiClient& client){
int res = client.read();
if(res < 0) {
// keep trying until you either read a valid byte or timeout
unsigned long startMillis = millis();
long timeoutIntervalMillis = client.getTimeout();
boolean timedOut = false;
for(;;) {
if (!client.connected()) return -1;
// loosely modeled after blinkWithoutDelay pattern
while(!timedOut && !client.available() && client.connected()){
delay(2);
timedOut = millis() - startMillis >= timeoutIntervalMillis;
}
res = client.read();
if(res >= 0) {
return res; // exit on a valid read
}
// NOTE: it is possible to get here and have all of the following
// assertions hold true
//
// -- client.available() > 0
// -- client.connected == true
// -- res == -1
//
// a simple retry strategy overcomes this which is to say the
// assertion is not permanent, but the reason that this works
// is elusive, and possibly indicative of a more subtle underlying
// issue
timedOut = millis() - startMillis >= timeoutIntervalMillis;
if(timedOut) {
return res; // exit on a timeout
}
}
}
return res;
}
bool WebServer::_parseForm(WiFiClient& client, String boundary, uint32_t len){
(void) len;
log_v("Parse Form: Boundary: %s Length: %d", boundary.c_str(), len);
String line;
int retry = 0;
do {
line = client.readStringUntil('\r');
++retry;
} while (line.length() == 0 && retry < 3);
client.readStringUntil('\n');
//start reading the form
if (line == ("--"+boundary)){
if(_postArgs) delete[] _postArgs;
_postArgs = new RequestArgument[WEBSERVER_MAX_POST_ARGS];
_postArgsLen = 0;
while(1){
String argName;
String argValue;
String argType;
String argFilename;
bool argIsFile = false;
line = client.readStringUntil('\r');
client.readStringUntil('\n');
if (line.length() > 19 && line.substring(0, 19).equalsIgnoreCase(F("Content-Disposition"))){
int nameStart = line.indexOf('=');
if (nameStart != -1){
argName = line.substring(nameStart+2);
nameStart = argName.indexOf('=');
if (nameStart == -1){
argName = argName.substring(0, argName.length() - 1);
} else {
argFilename = argName.substring(nameStart+2, argName.length() - 1);
argName = argName.substring(0, argName.indexOf('"'));
argIsFile = true;
log_v("PostArg FileName: %s",argFilename.c_str());
//use GET to set the filename if uploading using blob
if (argFilename == F("blob") && hasArg(FPSTR(filename)))
argFilename = arg(FPSTR(filename));
}
log_v("PostArg Name: %s", argName.c_str());
using namespace mime;
argType = FPSTR(mimeTable[txt].mimeType);
line = client.readStringUntil('\r');
client.readStringUntil('\n');
if (line.length() > 12 && line.substring(0, 12).equalsIgnoreCase(FPSTR(Content_Type))){
argType = line.substring(line.indexOf(':')+2);
//skip next line
client.readStringUntil('\r');
client.readStringUntil('\n');
}
log_v("PostArg Type: %s", argType.c_str());
if (!argIsFile){
while(1){
line = client.readStringUntil('\r');
client.readStringUntil('\n');
if (line.startsWith("--"+boundary)) break;
if (argValue.length() > 0) argValue += "\n";
argValue += line;
}
log_v("PostArg Value: %s", argValue.c_str());
RequestArgument& arg = _postArgs[_postArgsLen++];
arg.key = argName;
arg.value = argValue;
if (line == ("--"+boundary+"--")){
log_v("Done Parsing POST");
break;
} else if (_postArgsLen >= WEBSERVER_MAX_POST_ARGS) {
log_e("Too many PostArgs (max: %d) in request.", WEBSERVER_MAX_POST_ARGS);
return false;
}
} else {
_currentUpload.reset(new HTTPUpload());
_currentUpload->status = UPLOAD_FILE_START;
_currentUpload->name = argName;
_currentUpload->filename = argFilename;
_currentUpload->type = argType;
_currentUpload->totalSize = 0;
_currentUpload->currentSize = 0;
log_v("Start File: %s Type: %s", _currentUpload->filename.c_str(), _currentUpload->type.c_str());
if(_currentHandler && _currentHandler->canUpload(_currentUri))
_currentHandler->upload(*this, _currentUri, *_currentUpload);
_currentUpload->status = UPLOAD_FILE_WRITE;
int argByte = _uploadReadByte(client);
readfile:
while(argByte != 0x0D){
if(argByte < 0) return _parseFormUploadAborted();
_uploadWriteByte(argByte);
argByte = _uploadReadByte(client);
}
argByte = _uploadReadByte(client);
if(argByte < 0) return _parseFormUploadAborted();
if (argByte == 0x0A){
argByte = _uploadReadByte(client);
if(argByte < 0) return _parseFormUploadAborted();
if ((char)argByte != '-'){
//continue reading the file
_uploadWriteByte(0x0D);
_uploadWriteByte(0x0A);
goto readfile;
} else {
argByte = _uploadReadByte(client);
if(argByte < 0) return _parseFormUploadAborted();
if ((char)argByte != '-'){
//continue reading the file
_uploadWriteByte(0x0D);
_uploadWriteByte(0x0A);
_uploadWriteByte((uint8_t)('-'));
goto readfile;
}
}
uint8_t endBuf[boundary.length()];
uint32_t i = 0;
while(i < boundary.length()){
argByte = _uploadReadByte(client);
if(argByte < 0) return _parseFormUploadAborted();
if ((char)argByte == 0x0D){
_uploadWriteByte(0x0D);
_uploadWriteByte(0x0A);
_uploadWriteByte((uint8_t)('-'));
_uploadWriteByte((uint8_t)('-'));
uint32_t j = 0;
while(j < i){
_uploadWriteByte(endBuf[j++]);
}
goto readfile;
}
endBuf[i++] = (uint8_t)argByte;
}
if (strstr((const char*)endBuf, boundary.c_str()) != NULL){
if(_currentHandler && _currentHandler->canUpload(_currentUri))
_currentHandler->upload(*this, _currentUri, *_currentUpload);
_currentUpload->totalSize += _currentUpload->currentSize;
_currentUpload->status = UPLOAD_FILE_END;
if(_currentHandler && _currentHandler->canUpload(_currentUri))
_currentHandler->upload(*this, _currentUri, *_currentUpload);
log_v("End File: %s Type: %s Size: %d", _currentUpload->filename.c_str(), _currentUpload->type.c_str(), _currentUpload->totalSize);
line = client.readStringUntil(0x0D);
client.readStringUntil(0x0A);
if (line == "--"){
log_v("Done Parsing POST");
break;
}
continue;
} else {
_uploadWriteByte(0x0D);
_uploadWriteByte(0x0A);
_uploadWriteByte((uint8_t)('-'));
_uploadWriteByte((uint8_t)('-'));
uint32_t i = 0;
while(i < boundary.length()){
_uploadWriteByte(endBuf[i++]);
}
argByte = _uploadReadByte(client);
goto readfile;
}
} else {
_uploadWriteByte(0x0D);
goto readfile;
}
break;
}
}
}
}
int iarg;
int totalArgs = ((WEBSERVER_MAX_POST_ARGS - _postArgsLen) < _currentArgCount)?(WEBSERVER_MAX_POST_ARGS - _postArgsLen):_currentArgCount;
for (iarg = 0; iarg < totalArgs; iarg++){
RequestArgument& arg = _postArgs[_postArgsLen++];
arg.key = _currentArgs[iarg].key;
arg.value = _currentArgs[iarg].value;
}
if (_currentArgs) delete[] _currentArgs;
_currentArgs = new RequestArgument[_postArgsLen];
for (iarg = 0; iarg < _postArgsLen; iarg++){
RequestArgument& arg = _currentArgs[iarg];
arg.key = _postArgs[iarg].key;
arg.value = _postArgs[iarg].value;
}
_currentArgCount = iarg;
if (_postArgs) {
delete[] _postArgs;
_postArgs=nullptr;
_postArgsLen = 0;
}
return true;
}
log_e("Error: line: %s", line.c_str());
return false;
}
String WebServer::urlDecode(const String& text)
{
String decoded = "";
char temp[] = "0x00";
unsigned int len = text.length();
unsigned int i = 0;
while (i < len)
{
char decodedChar;
char encodedChar = text.charAt(i++);
if ((encodedChar == '%') && (i + 1 < len))
{
temp[2] = text.charAt(i++);
temp[3] = text.charAt(i++);
decodedChar = strtol(temp, NULL, 16);
}
else {
if (encodedChar == '+')
{
decodedChar = ' ';
}
else {
decodedChar = encodedChar; // normal ascii char
}
}
decoded += decodedChar;
}
return decoded;
}
bool WebServer::_parseFormUploadAborted(){
_currentUpload->status = UPLOAD_FILE_ABORTED;
if(_currentHandler && _currentHandler->canUpload(_currentUri))
_currentHandler->upload(*this, _currentUri, *_currentUpload);
return false;
}

View File

@ -0,0 +1,29 @@
#ifndef URI_H
#define URI_H
#include <Arduino.h>
#include <vector>
class Uri {
protected:
const String _uri;
public:
Uri(const char *uri) : _uri(uri) {}
Uri(const String &uri) : _uri(uri) {}
Uri(const __FlashStringHelper *uri) : _uri(String(uri)) {}
virtual ~Uri() {}
virtual Uri* clone() const {
return new Uri(_uri);
};
virtual void initPathArgs(__attribute__((unused)) std::vector<String> &pathArgs) {}
virtual bool canHandle(const String &requestUri, __attribute__((unused)) std::vector<String> &pathArgs) {
return _uri == requestUri;
}
};
#endif

View File

@ -0,0 +1,723 @@
/*
WebServer.cpp - Dead simple web-server.
Supports only one simultaneous client, knows how to handle GET and POST.
Copyright (c) 2014 Ivan Grokhotkov. All rights reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
Modified 8 May 2015 by Hristo Gochkov (proper post and file upload handling)
*/
#include <Arduino.h>
#include <esp32-hal-log.h>
#include <libb64/cencode.h>
#include "WiFiServer.h"
#include "WiFiClient.h"
#include "WebServer.h"
#include "FS.h"
#include "detail/RequestHandlersImpl.h"
#include "mbedtls/md5.h"
static const char AUTHORIZATION_HEADER[] = "Authorization";
static const char qop_auth[] PROGMEM = "qop=auth";
static const char qop_auth_quoted[] PROGMEM = "qop=\"auth\"";
static const char WWW_Authenticate[] = "WWW-Authenticate";
static const char Content_Length[] = "Content-Length";
WebServer::WebServer(IPAddress addr, int port)
: _corsEnabled(false)
, _server(addr, port)
, _currentMethod(HTTP_ANY)
, _currentVersion(0)
, _currentStatus(HC_NONE)
, _statusChange(0)
, _nullDelay(true)
, _currentHandler(nullptr)
, _firstHandler(nullptr)
, _lastHandler(nullptr)
, _currentArgCount(0)
, _currentArgs(nullptr)
, _postArgsLen(0)
, _postArgs(nullptr)
, _headerKeysCount(0)
, _currentHeaders(nullptr)
, _contentLength(0)
, _clientContentLength(0)
, _chunked(false)
{
log_v("WebServer::Webserver(addr=%s, port=%d)", addr.toString().c_str(), port);
}
WebServer::WebServer(int port)
: _corsEnabled(false)
, _server(port)
, _currentMethod(HTTP_ANY)
, _currentVersion(0)
, _currentStatus(HC_NONE)
, _statusChange(0)
, _nullDelay(true)
, _currentHandler(nullptr)
, _firstHandler(nullptr)
, _lastHandler(nullptr)
, _currentArgCount(0)
, _currentArgs(nullptr)
, _postArgsLen(0)
, _postArgs(nullptr)
, _headerKeysCount(0)
, _currentHeaders(nullptr)
, _contentLength(0)
, _clientContentLength(0)
, _chunked(false)
{
log_v("WebServer::Webserver(port=%d)", port);
}
WebServer::~WebServer() {
_server.close();
if (_currentHeaders)
delete[]_currentHeaders;
RequestHandler* handler = _firstHandler;
while (handler) {
RequestHandler* next = handler->next();
delete handler;
handler = next;
}
}
void WebServer::begin() {
close();
_server.begin();
_server.setNoDelay(true);
}
void WebServer::begin(uint16_t port) {
close();
_server.begin(port);
_server.setNoDelay(true);
}
String WebServer::_extractParam(String& authReq,const String& param,const char delimit){
int _begin = authReq.indexOf(param);
if (_begin == -1)
return "";
return authReq.substring(_begin+param.length(),authReq.indexOf(delimit,_begin+param.length()));
}
static String md5str(String &in){
char out[33] = {0};
mbedtls_md5_context _ctx;
uint8_t i;
uint8_t * _buf = (uint8_t*)malloc(16);
if(_buf == NULL)
return String(out);
memset(_buf, 0x00, 16);
mbedtls_md5_init(&_ctx);
mbedtls_md5_starts_ret(&_ctx);
mbedtls_md5_update_ret(&_ctx, (const uint8_t *)in.c_str(), in.length());
mbedtls_md5_finish_ret(&_ctx, _buf);
for(i = 0; i < 16; i++) {
sprintf(out + (i * 2), "%02x", _buf[i]);
}
out[32] = 0;
free(_buf);
return String(out);
}
bool WebServer::authenticate(const char * username, const char * password){
if(hasHeader(FPSTR(AUTHORIZATION_HEADER))) {
String authReq = header(FPSTR(AUTHORIZATION_HEADER));
if(authReq.startsWith(F("Basic"))){
authReq = authReq.substring(6);
authReq.trim();
char toencodeLen = strlen(username)+strlen(password)+1;
char *toencode = new char[toencodeLen + 1];
if(toencode == NULL){
authReq = "";
return false;
}
char *encoded = new char[base64_encode_expected_len(toencodeLen)+1];
if(encoded == NULL){
authReq = "";
delete[] toencode;
return false;
}
sprintf(toencode, "%s:%s", username, password);
if(base64_encode_chars(toencode, toencodeLen, encoded) > 0 && authReq.equalsConstantTime(encoded)) {
authReq = "";
delete[] toencode;
delete[] encoded;
return true;
}
delete[] toencode;
delete[] encoded;
} else if(authReq.startsWith(F("Digest"))) {
authReq = authReq.substring(7);
log_v("%s", authReq.c_str());
String _username = _extractParam(authReq,F("username=\""),'\"');
if(!_username.length() || _username != String(username)) {
authReq = "";
return false;
}
// extracting required parameters for RFC 2069 simpler Digest
String _realm = _extractParam(authReq, F("realm=\""),'\"');
String _nonce = _extractParam(authReq, F("nonce=\""),'\"');
String _uri = _extractParam(authReq, F("uri=\""),'\"');
String _response = _extractParam(authReq, F("response=\""),'\"');
String _opaque = _extractParam(authReq, F("opaque=\""),'\"');
if((!_realm.length()) || (!_nonce.length()) || (!_uri.length()) || (!_response.length()) || (!_opaque.length())) {
authReq = "";
return false;
}
if((_opaque != _sopaque) || (_nonce != _snonce) || (_realm != _srealm)) {
authReq = "";
return false;
}
// parameters for the RFC 2617 newer Digest
String _nc,_cnonce;
if(authReq.indexOf(FPSTR(qop_auth)) != -1 || authReq.indexOf(FPSTR(qop_auth_quoted)) != -1) {
_nc = _extractParam(authReq, F("nc="), ',');
_cnonce = _extractParam(authReq, F("cnonce=\""),'\"');
}
String _H1 = md5str(String(username) + ':' + _realm + ':' + String(password));
log_v("Hash of user:realm:pass=%s", _H1.c_str());
String _H2 = "";
if(_currentMethod == HTTP_GET){
_H2 = md5str(String(F("GET:")) + _uri);
}else if(_currentMethod == HTTP_POST){
_H2 = md5str(String(F("POST:")) + _uri);
}else if(_currentMethod == HTTP_PUT){
_H2 = md5str(String(F("PUT:")) + _uri);
}else if(_currentMethod == HTTP_DELETE){
_H2 = md5str(String(F("DELETE:")) + _uri);
}else{
_H2 = md5str(String(F("GET:")) + _uri);
}
log_v("Hash of GET:uri=%s", _H2.c_str());
String _responsecheck = "";
if(authReq.indexOf(FPSTR(qop_auth)) != -1 || authReq.indexOf(FPSTR(qop_auth_quoted)) != -1) {
_responsecheck = md5str(_H1 + ':' + _nonce + ':' + _nc + ':' + _cnonce + F(":auth:") + _H2);
} else {
_responsecheck = md5str(_H1 + ':' + _nonce + ':' + _H2);
}
log_v("The Proper response=%s", _responsecheck.c_str());
if(_response == _responsecheck){
authReq = "";
return true;
}
}
authReq = "";
}
return false;
}
String WebServer::_getRandomHexString() {
char buffer[33]; // buffer to hold 32 Hex Digit + /0
int i;
for(i = 0; i < 4; i++) {
sprintf (buffer + (i*8), "%08x", esp_random());
}
return String(buffer);
}
void WebServer::requestAuthentication(HTTPAuthMethod mode, const char* realm, const String& authFailMsg) {
if(realm == NULL) {
_srealm = String(F("Login Required"));
} else {
_srealm = String(realm);
}
if(mode == BASIC_AUTH) {
sendHeader(String(FPSTR(WWW_Authenticate)), String(F("Basic realm=\"")) + _srealm + String(F("\"")));
} else {
_snonce=_getRandomHexString();
_sopaque=_getRandomHexString();
sendHeader(String(FPSTR(WWW_Authenticate)), String(F("Digest realm=\"")) +_srealm + String(F("\", qop=\"auth\", nonce=\"")) + _snonce + String(F("\", opaque=\"")) + _sopaque + String(F("\"")));
}
using namespace mime;
send(401, String(FPSTR(mimeTable[html].mimeType)), authFailMsg);
}
void WebServer::on(const Uri &uri, WebServer::THandlerFunction handler) {
on(uri, HTTP_ANY, handler);
}
void WebServer::on(const Uri &uri, HTTPMethod method, WebServer::THandlerFunction fn) {
on(uri, method, fn, _fileUploadHandler);
}
void WebServer::on(const Uri &uri, HTTPMethod method, WebServer::THandlerFunction fn, WebServer::THandlerFunction ufn) {
_addRequestHandler(new FunctionRequestHandler(fn, ufn, uri, method));
}
void WebServer::addHandler(RequestHandler* handler) {
_addRequestHandler(handler);
}
void WebServer::_addRequestHandler(RequestHandler* handler) {
if (!_lastHandler) {
_firstHandler = handler;
_lastHandler = handler;
}
else {
_lastHandler->next(handler);
_lastHandler = handler;
}
}
void WebServer::serveStatic(const char* uri, FS& fs, const char* path, const char* cache_header) {
_addRequestHandler(new StaticRequestHandler(fs, path, uri, cache_header));
}
void WebServer::handleClient() {
if (_currentStatus == HC_NONE) {
WiFiClient client = _server.available();
if (!client) {
if (_nullDelay) {
delay(1);
}
return;
}
log_v("New client: client.localIP()=%s", client.localIP().toString().c_str());
_currentClient = client;
_currentStatus = HC_WAIT_READ;
_statusChange = millis();
}
bool keepCurrentClient = false;
bool callYield = false;
if (_currentClient.connected()) {
switch (_currentStatus) {
case HC_NONE:
// No-op to avoid C++ compiler warning
break;
case HC_WAIT_READ:
// Wait for data from client to become available
if (_currentClient.available()) {
if (_parseRequest(_currentClient)) {
// because HTTP_MAX_SEND_WAIT is expressed in milliseconds,
// it must be divided by 1000
_currentClient.setTimeout(HTTP_MAX_SEND_WAIT / 1000);
_contentLength = CONTENT_LENGTH_NOT_SET;
_handleRequest();
// Fix for issue with Chrome based browsers: https://github.com/espressif/arduino-esp32/issues/3652
// if (_currentClient.connected()) {
// _currentStatus = HC_WAIT_CLOSE;
// _statusChange = millis();
// keepCurrentClient = true;
// }
}
} else { // !_currentClient.available()
if (millis() - _statusChange <= HTTP_MAX_DATA_WAIT) {
keepCurrentClient = true;
}
callYield = true;
}
break;
case HC_WAIT_CLOSE:
// Wait for client to close the connection
if (millis() - _statusChange <= HTTP_MAX_CLOSE_WAIT) {
keepCurrentClient = true;
callYield = true;
}
}
}
if (!keepCurrentClient) {
_currentClient = WiFiClient();
_currentStatus = HC_NONE;
_currentUpload.reset();
}
if (callYield) {
yield();
}
}
void WebServer::close() {
_server.close();
_currentStatus = HC_NONE;
if(!_headerKeysCount)
collectHeaders(0, 0);
}
void WebServer::stop() {
close();
}
void WebServer::sendHeader(const String& name, const String& value, bool first) {
String headerLine = name;
headerLine += F(": ");
headerLine += value;
headerLine += "\r\n";
if (first) {
_responseHeaders = headerLine + _responseHeaders;
}
else {
_responseHeaders += headerLine;
}
}
void WebServer::setContentLength(const size_t contentLength) {
_contentLength = contentLength;
}
void WebServer::enableDelay(boolean value) {
_nullDelay = value;
}
void WebServer::enableCORS(boolean value) {
_corsEnabled = value;
}
void WebServer::enableCrossOrigin(boolean value) {
enableCORS(value);
}
void WebServer::_prepareHeader(String& response, int code, const char* content_type, size_t contentLength) {
response = String(F("HTTP/1.")) + String(_currentVersion) + ' ';
response += String(code);
response += ' ';
response += _responseCodeToString(code);
response += "\r\n";
using namespace mime;
if (!content_type)
content_type = mimeTable[html].mimeType;
sendHeader(String(F("Content-Type")), String(FPSTR(content_type)), true);
if (_contentLength == CONTENT_LENGTH_NOT_SET) {
sendHeader(String(FPSTR(Content_Length)), String(contentLength));
} else if (_contentLength != CONTENT_LENGTH_UNKNOWN) {
sendHeader(String(FPSTR(Content_Length)), String(_contentLength));
} else if(_contentLength == CONTENT_LENGTH_UNKNOWN && _currentVersion){ //HTTP/1.1 or above client
//let's do chunked
_chunked = true;
sendHeader(String(F("Accept-Ranges")),String(F("none")));
sendHeader(String(F("Transfer-Encoding")),String(F("chunked")));
}
if (_corsEnabled) {
sendHeader(String(FPSTR("Access-Control-Allow-Origin")), String("*"));
sendHeader(String(FPSTR("Access-Control-Allow-Methods")), String("*"));
sendHeader(String(FPSTR("Access-Control-Allow-Headers")), String("*"));
}
sendHeader(String(F("Connection")), String(F("close")));
response += _responseHeaders;
response += "\r\n";
_responseHeaders = "";
}
void WebServer::send(int code, const char* content_type, const String& content) {
String header;
// Can we asume the following?
//if(code == 200 && content.length() == 0 && _contentLength == CONTENT_LENGTH_NOT_SET)
// _contentLength = CONTENT_LENGTH_UNKNOWN;
if (content.length() == 0) {
log_w("content length is zero");
}
_prepareHeader(header, code, content_type, content.length());
_currentClientWrite(header.c_str(), header.length());
if(content.length())
sendContent(content);
}
void WebServer::send(int code, char* content_type, const String& content) {
send(code, (const char*)content_type, content);
}
void WebServer::send(int code, const String& content_type, const String& content) {
send(code, (const char*)content_type.c_str(), content);
}
void WebServer::send(int code, const char* content_type, const char* content)
{
const String passStr = (String)content;
if (strlen(content) != passStr.length()) {
log_e("String cast failed. Use send_P for long arrays");
}
send(code, content_type, passStr);
}
void WebServer::send_P(int code, PGM_P content_type, PGM_P content) {
size_t contentLength = 0;
if (content != NULL) {
contentLength = strlen_P(content);
}
String header;
char type[64];
memccpy_P((void*)type, (PGM_VOID_P)content_type, 0, sizeof(type));
_prepareHeader(header, code, (const char* )type, contentLength);
_currentClientWrite(header.c_str(), header.length());
sendContent_P(content);
}
void WebServer::send_P(int code, PGM_P content_type, PGM_P content, size_t contentLength) {
String header;
char type[64];
memccpy_P((void*)type, (PGM_VOID_P)content_type, 0, sizeof(type));
_prepareHeader(header, code, (const char* )type, contentLength);
sendContent(header);
sendContent_P(content, contentLength);
}
void WebServer::sendContent(const String& content) {
sendContent(content.c_str(), content.length());
}
void WebServer::sendContent(const char* content, size_t contentLength) {
const char * footer = "\r\n";
if(_chunked) {
char * chunkSize = (char *)malloc(11);
if(chunkSize){
sprintf(chunkSize, "%x%s", contentLength, footer);
_currentClientWrite(chunkSize, strlen(chunkSize));
free(chunkSize);
}
}
_currentClientWrite(content, contentLength);
if(_chunked){
_currentClient.write(footer, 2);
if (contentLength == 0) {
_chunked = false;
}
}
}
void WebServer::sendContent_P(PGM_P content) {
sendContent_P(content, strlen_P(content));
}
void WebServer::sendContent_P(PGM_P content, size_t size) {
const char * footer = "\r\n";
if(_chunked) {
char * chunkSize = (char *)malloc(11);
if(chunkSize){
sprintf(chunkSize, "%x%s", size, footer);
_currentClientWrite(chunkSize, strlen(chunkSize));
free(chunkSize);
}
}
_currentClientWrite_P(content, size);
if(_chunked){
_currentClient.write(footer, 2);
if (size == 0) {
_chunked = false;
}
}
}
void WebServer::_streamFileCore(const size_t fileSize, const String & fileName, const String & contentType, const int code)
{
using namespace mime;
setContentLength(fileSize);
if (fileName.endsWith(String(FPSTR(mimeTable[gz].endsWith))) &&
contentType != String(FPSTR(mimeTable[gz].mimeType)) &&
contentType != String(FPSTR(mimeTable[none].mimeType))) {
sendHeader(F("Content-Encoding"), F("gzip"));
}
send(code, contentType, "");
}
String WebServer::pathArg(unsigned int i) {
if (_currentHandler != nullptr)
return _currentHandler->pathArg(i);
return "";
}
String WebServer::arg(String name) {
for (int j = 0; j < _postArgsLen; ++j) {
if ( _postArgs[j].key == name )
return _postArgs[j].value;
}
for (int i = 0; i < _currentArgCount; ++i) {
if ( _currentArgs[i].key == name )
return _currentArgs[i].value;
}
return "";
}
String WebServer::arg(int i) {
if (i < _currentArgCount)
return _currentArgs[i].value;
return "";
}
String WebServer::argName(int i) {
if (i < _currentArgCount)
return _currentArgs[i].key;
return "";
}
int WebServer::args() {
return _currentArgCount;
}
bool WebServer::hasArg(String name) {
for (int j = 0; j < _postArgsLen; ++j) {
if (_postArgs[j].key == name)
return true;
}
for (int i = 0; i < _currentArgCount; ++i) {
if (_currentArgs[i].key == name)
return true;
}
return false;
}
String WebServer::header(String name) {
for (int i = 0; i < _headerKeysCount; ++i) {
if (_currentHeaders[i].key.equalsIgnoreCase(name))
return _currentHeaders[i].value;
}
return "";
}
void WebServer::collectHeaders(const char* headerKeys[], const size_t headerKeysCount) {
_headerKeysCount = headerKeysCount + 1;
if (_currentHeaders)
delete[]_currentHeaders;
_currentHeaders = new RequestArgument[_headerKeysCount];
_currentHeaders[0].key = FPSTR(AUTHORIZATION_HEADER);
for (int i = 1; i < _headerKeysCount; i++){
_currentHeaders[i].key = headerKeys[i-1];
}
}
String WebServer::header(int i) {
if (i < _headerKeysCount)
return _currentHeaders[i].value;
return "";
}
String WebServer::headerName(int i) {
if (i < _headerKeysCount)
return _currentHeaders[i].key;
return "";
}
int WebServer::headers() {
return _headerKeysCount;
}
bool WebServer::hasHeader(String name) {
for (int i = 0; i < _headerKeysCount; ++i) {
if ((_currentHeaders[i].key.equalsIgnoreCase(name)) && (_currentHeaders[i].value.length() > 0))
return true;
}
return false;
}
String WebServer::hostHeader() {
return _hostHeader;
}
void WebServer::onFileUpload(THandlerFunction fn) {
_fileUploadHandler = fn;
}
void WebServer::onNotFound(THandlerFunction fn) {
_notFoundHandler = fn;
}
void WebServer::_handleRequest() {
bool handled = false;
if (!_currentHandler){
log_e("request handler not found");
}
else {
handled = _currentHandler->handle(*this, _currentMethod, _currentUri);
if (!handled) {
log_e("request handler failed to handle request");
}
}
if (!handled && _notFoundHandler) {
_notFoundHandler();
handled = true;
}
if (!handled) {
using namespace mime;
send(404, String(FPSTR(mimeTable[html].mimeType)), String(F("Not found: ")) + _currentUri);
handled = true;
}
if (handled) {
_finalizeResponse();
}
_currentUri = "";
}
void WebServer::_finalizeResponse() {
if (_chunked) {
sendContent("");
}
}
String WebServer::_responseCodeToString(int code) {
switch (code) {
case 100: return F("Continue");
case 101: return F("Switching Protocols");
case 200: return F("OK");
case 201: return F("Created");
case 202: return F("Accepted");
case 203: return F("Non-Authoritative Information");
case 204: return F("No Content");
case 205: return F("Reset Content");
case 206: return F("Partial Content");
case 300: return F("Multiple Choices");
case 301: return F("Moved Permanently");
case 302: return F("Found");
case 303: return F("See Other");
case 304: return F("Not Modified");
case 305: return F("Use Proxy");
case 307: return F("Temporary Redirect");
case 400: return F("Bad Request");
case 401: return F("Unauthorized");
case 402: return F("Payment Required");
case 403: return F("Forbidden");
case 404: return F("Not Found");
case 405: return F("Method Not Allowed");
case 406: return F("Not Acceptable");
case 407: return F("Proxy Authentication Required");
case 408: return F("Request Time-out");
case 409: return F("Conflict");
case 410: return F("Gone");
case 411: return F("Length Required");
case 412: return F("Precondition Failed");
case 413: return F("Request Entity Too Large");
case 414: return F("Request-URI Too Large");
case 415: return F("Unsupported Media Type");
case 416: return F("Requested range not satisfiable");
case 417: return F("Expectation Failed");
case 500: return F("Internal Server Error");
case 501: return F("Not Implemented");
case 502: return F("Bad Gateway");
case 503: return F("Service Unavailable");
case 504: return F("Gateway Time-out");
case 505: return F("HTTP Version not supported");
default: return F("");
}
}

View File

@ -0,0 +1,216 @@
/*
WebServer.h - Dead simple web-server.
Supports only one simultaneous client, knows how to handle GET and POST.
Copyright (c) 2014 Ivan Grokhotkov. All rights reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
Modified 8 May 2015 by Hristo Gochkov (proper post and file upload handling)
*/
#ifndef WEBSERVER_H
#define WEBSERVER_H
#include <functional>
#include <memory>
#include <WiFi.h>
#include "HTTP_Method.h"
#include "Uri.h"
enum HTTPUploadStatus { UPLOAD_FILE_START, UPLOAD_FILE_WRITE, UPLOAD_FILE_END,
UPLOAD_FILE_ABORTED };
enum HTTPClientStatus { HC_NONE, HC_WAIT_READ, HC_WAIT_CLOSE };
enum HTTPAuthMethod { BASIC_AUTH, DIGEST_AUTH };
#define HTTP_DOWNLOAD_UNIT_SIZE 1436
#ifndef HTTP_UPLOAD_BUFLEN
#define HTTP_UPLOAD_BUFLEN 1436
#endif
#define HTTP_MAX_DATA_WAIT 5000 //ms to wait for the client to send the request
#define HTTP_MAX_POST_WAIT 5000 //ms to wait for POST data to arrive
#define HTTP_MAX_SEND_WAIT 5000 //ms to wait for data chunk to be ACKed
#define HTTP_MAX_CLOSE_WAIT 2000 //ms to wait for the client to close the connection
#define CONTENT_LENGTH_UNKNOWN ((size_t) -1)
#define CONTENT_LENGTH_NOT_SET ((size_t) -2)
class WebServer;
typedef struct {
HTTPUploadStatus status;
String filename;
String name;
String type;
size_t totalSize; // file size
size_t currentSize; // size of data currently in buf
uint8_t buf[HTTP_UPLOAD_BUFLEN];
} HTTPUpload;
#include "detail/RequestHandler.h"
namespace fs {
class FS;
}
class WebServer
{
public:
WebServer(IPAddress addr, int port = 80);
WebServer(int port = 80);
virtual ~WebServer();
virtual void begin();
virtual void begin(uint16_t port);
virtual void handleClient();
virtual void close();
void stop();
bool authenticate(const char * username, const char * password);
void requestAuthentication(HTTPAuthMethod mode = BASIC_AUTH, const char* realm = NULL, const String& authFailMsg = String("") );
typedef std::function<void(void)> THandlerFunction;
void on(const Uri &uri, THandlerFunction fn);
void on(const Uri &uri, HTTPMethod method, THandlerFunction fn);
void on(const Uri &uri, HTTPMethod method, THandlerFunction fn, THandlerFunction ufn); //ufn handles file uploads
void addHandler(RequestHandler* handler);
void serveStatic(const char* uri, fs::FS& fs, const char* path, const char* cache_header = NULL );
void onNotFound(THandlerFunction fn); //called when handler is not assigned
void onFileUpload(THandlerFunction ufn); //handle file uploads
String uri() { return _currentUri; }
HTTPMethod method() { return _currentMethod; }
virtual WiFiClient client() { return _currentClient; }
HTTPUpload& upload() { return *_currentUpload; }
String pathArg(unsigned int i); // get request path argument by number
String arg(String name); // get request argument value by name
String arg(int i); // get request argument value by number
String argName(int i); // get request argument name by number
int args(); // get arguments count
bool hasArg(String name); // check if argument exists
void collectHeaders(const char* headerKeys[], const size_t headerKeysCount); // set the request headers to collect
String header(String name); // get request header value by name
String header(int i); // get request header value by number
String headerName(int i); // get request header name by number
int headers(); // get header count
bool hasHeader(String name); // check if header exists
int clientContentLength() { return _clientContentLength; } // return "content-length" of incoming HTTP header from "_currentClient"
String hostHeader(); // get request host header if available or empty String if not
// send response to the client
// code - HTTP response code, can be 200 or 404
// content_type - HTTP content type, like "text/plain" or "image/png"
// content - actual content body
void send(int code, const char* content_type = NULL, const String& content = String(""));
void send(int code, char* content_type, const String& content);
void send(int code, const String& content_type, const String& content);
void send(int code, const char* content_type, const char* content);
void send_P(int code, PGM_P content_type, PGM_P content);
void send_P(int code, PGM_P content_type, PGM_P content, size_t contentLength);
void enableDelay(boolean value);
void enableCORS(boolean value = true);
void enableCrossOrigin(boolean value = true);
void setContentLength(const size_t contentLength);
void sendHeader(const String& name, const String& value, bool first = false);
void sendContent(const String& content);
void sendContent(const char* content, size_t contentLength);
void sendContent_P(PGM_P content);
void sendContent_P(PGM_P content, size_t size);
static String urlDecode(const String& text);
template<typename T>
size_t streamFile(T &file, const String& contentType, const int code = 200) {
_streamFileCore(file.size(), file.name(), contentType, code);
return _currentClient.write(file);
}
protected:
virtual size_t _currentClientWrite(const char* b, size_t l) { return _currentClient.write( b, l ); }
virtual size_t _currentClientWrite_P(PGM_P b, size_t l) { return _currentClient.write_P( b, l ); }
void _addRequestHandler(RequestHandler* handler);
void _handleRequest();
void _finalizeResponse();
bool _parseRequest(WiFiClient& client);
void _parseArguments(String data);
static String _responseCodeToString(int code);
bool _parseForm(WiFiClient& client, String boundary, uint32_t len);
bool _parseFormUploadAborted();
void _uploadWriteByte(uint8_t b);
int _uploadReadByte(WiFiClient& client);
void _prepareHeader(String& response, int code, const char* content_type, size_t contentLength);
bool _collectHeader(const char* headerName, const char* headerValue);
void _streamFileCore(const size_t fileSize, const String & fileName, const String & contentType, const int code = 200);
String _getRandomHexString();
// for extracting Auth parameters
String _extractParam(String& authReq,const String& param,const char delimit = '"');
struct RequestArgument {
String key;
String value;
};
boolean _corsEnabled;
WiFiServer _server;
WiFiClient _currentClient;
HTTPMethod _currentMethod;
String _currentUri;
uint8_t _currentVersion;
HTTPClientStatus _currentStatus;
unsigned long _statusChange;
boolean _nullDelay;
RequestHandler* _currentHandler;
RequestHandler* _firstHandler;
RequestHandler* _lastHandler;
THandlerFunction _notFoundHandler;
THandlerFunction _fileUploadHandler;
int _currentArgCount;
RequestArgument* _currentArgs;
int _postArgsLen;
RequestArgument* _postArgs;
std::unique_ptr<HTTPUpload> _currentUpload;
int _headerKeysCount;
RequestArgument* _currentHeaders;
size_t _contentLength;
int _clientContentLength; // "Content-Length" from header of incoming POST or GET request
String _responseHeaders;
String _hostHeader;
bool _chunked;
String _snonce; // Store noance and opaque for future comparison
String _sopaque;
String _srealm; // Store the Auth realm between Calls
};
#endif //ESP8266WEBSERVER_H

View File

@ -0,0 +1,31 @@
#ifndef REQUESTHANDLER_H
#define REQUESTHANDLER_H
#include <vector>
#include <assert.h>
class RequestHandler {
public:
virtual ~RequestHandler() { }
virtual bool canHandle(HTTPMethod method, String uri) { (void) method; (void) uri; return false; }
virtual bool canUpload(String uri) { (void) uri; return false; }
virtual bool handle(WebServer& server, HTTPMethod requestMethod, String requestUri) { (void) server; (void) requestMethod; (void) requestUri; return false; }
virtual void upload(WebServer& server, String requestUri, HTTPUpload& upload) { (void) server; (void) requestUri; (void) upload; }
RequestHandler* next() { return _next; }
void next(RequestHandler* r) { _next = r; }
private:
RequestHandler* _next = nullptr;
protected:
std::vector<String> pathArgs;
public:
const String& pathArg(unsigned int i) {
assert(i < pathArgs.size());
return pathArgs[i];
}
};
#endif //REQUESTHANDLER_H

View File

@ -0,0 +1,152 @@
#ifndef REQUESTHANDLERSIMPL_H
#define REQUESTHANDLERSIMPL_H
#include "RequestHandler.h"
#include "mimetable.h"
#include "WString.h"
#include "Uri.h"
using namespace mime;
class FunctionRequestHandler : public RequestHandler {
public:
FunctionRequestHandler(WebServer::THandlerFunction fn, WebServer::THandlerFunction ufn, const Uri &uri, HTTPMethod method)
: _fn(fn)
, _ufn(ufn)
, _uri(uri.clone())
, _method(method)
{
_uri->initPathArgs(pathArgs);
}
~FunctionRequestHandler() {
delete _uri;
}
bool canHandle(HTTPMethod requestMethod, String requestUri) override {
if (_method != HTTP_ANY && _method != requestMethod)
return false;
return _uri->canHandle(requestUri, pathArgs);
}
bool canUpload(String requestUri) override {
if (!_ufn || !canHandle(HTTP_POST, requestUri))
return false;
return true;
}
bool handle(WebServer& server, HTTPMethod requestMethod, String requestUri) override {
(void) server;
if (!canHandle(requestMethod, requestUri))
return false;
_fn();
return true;
}
void upload(WebServer& server, String requestUri, HTTPUpload& upload) override {
(void) server;
(void) upload;
if (canUpload(requestUri))
_ufn();
}
protected:
WebServer::THandlerFunction _fn;
WebServer::THandlerFunction _ufn;
Uri *_uri;
HTTPMethod _method;
};
class StaticRequestHandler : public RequestHandler {
public:
StaticRequestHandler(FS& fs, const char* path, const char* uri, const char* cache_header)
: _fs(fs)
, _uri(uri)
, _path(path)
, _cache_header(cache_header)
{
File f = fs.open(path);
_isFile = (f && (! f.isDirectory()));
log_v("StaticRequestHandler: path=%s uri=%s isFile=%d, cache_header=%s\r\n", path, uri, _isFile, cache_header ? cache_header : ""); // issue 5506 - cache_header can be nullptr
_baseUriLength = _uri.length();
}
bool canHandle(HTTPMethod requestMethod, String requestUri) override {
if (requestMethod != HTTP_GET)
return false;
if ((_isFile && requestUri != _uri) || !requestUri.startsWith(_uri))
return false;
return true;
}
bool handle(WebServer& server, HTTPMethod requestMethod, String requestUri) override {
if (!canHandle(requestMethod, requestUri))
return false;
log_v("StaticRequestHandler::handle: request=%s _uri=%s\r\n", requestUri.c_str(), _uri.c_str());
String path(_path);
if (!_isFile) {
// Base URI doesn't point to a file.
// If a directory is requested, look for index file.
if (requestUri.endsWith("/"))
requestUri += "index.htm";
// Append whatever follows this URI in request to get the file path.
path += requestUri.substring(_baseUriLength);
}
log_v("StaticRequestHandler::handle: path=%s, isFile=%d\r\n", path.c_str(), _isFile);
String contentType = getContentType(path);
// look for gz file, only if the original specified path is not a gz. So part only works to send gzip via content encoding when a non compressed is asked for
// if you point the the path to gzip you will serve the gzip as content type "application/x-gzip", not text or javascript etc...
if (!path.endsWith(FPSTR(mimeTable[gz].endsWith)) && !_fs.exists(path)) {
String pathWithGz = path + FPSTR(mimeTable[gz].endsWith);
if(_fs.exists(pathWithGz))
path += FPSTR(mimeTable[gz].endsWith);
}
File f = _fs.open(path, "r");
if (!f || !f.available())
return false;
if (_cache_header.length() != 0)
server.sendHeader("Cache-Control", _cache_header);
server.streamFile(f, contentType);
return true;
}
static String getContentType(const String& path) {
char buff[sizeof(mimeTable[0].mimeType)];
// Check all entries but last one for match, return if found
for (size_t i=0; i < sizeof(mimeTable)/sizeof(mimeTable[0])-1; i++) {
strcpy_P(buff, mimeTable[i].endsWith);
if (path.endsWith(buff)) {
strcpy_P(buff, mimeTable[i].mimeType);
return String(buff);
}
}
// Fall-through and just return default type
strcpy_P(buff, mimeTable[sizeof(mimeTable)/sizeof(mimeTable[0])-1].mimeType);
return String(buff);
}
protected:
FS _fs;
String _uri;
String _path;
String _cache_header;
bool _isFile;
size_t _baseUriLength;
};
#endif //REQUESTHANDLERSIMPL_H

View File

@ -0,0 +1,35 @@
#include "mimetable.h"
#include "pgmspace.h"
namespace mime
{
// Table of extension->MIME strings stored in PROGMEM, needs to be global due to GCC section typing rules
const Entry mimeTable[maxType] =
{
{ ".html", "text/html" },
{ ".htm", "text/html" },
{ ".css", "text/css" },
{ ".txt", "text/plain" },
{ ".js", "application/javascript" },
{ ".json", "application/json" },
{ ".png", "image/png" },
{ ".gif", "image/gif" },
{ ".jpg", "image/jpeg" },
{ ".ico", "image/x-icon" },
{ ".svg", "image/svg+xml" },
{ ".ttf", "application/x-font-ttf" },
{ ".otf", "application/x-font-opentype" },
{ ".woff", "application/font-woff" },
{ ".woff2", "application/font-woff2" },
{ ".eot", "application/vnd.ms-fontobject" },
{ ".sfnt", "application/font-sfnt" },
{ ".xml", "text/xml" },
{ ".pdf", "application/pdf" },
{ ".zip", "application/zip" },
{ ".gz", "application/x-gzip" },
{ ".appcache", "text/cache-manifest" },
{ "", "application/octet-stream" }
};
}

View File

@ -0,0 +1,47 @@
#ifndef __MIMETABLE_H__
#define __MIMETABLE_H__
namespace mime
{
enum type
{
html,
htm,
css,
txt,
js,
json,
png,
gif,
jpg,
ico,
svg,
ttf,
otf,
woff,
woff2,
eot,
sfnt,
xml,
pdf,
zip,
gz,
appcache,
none,
maxType
};
struct Entry
{
const char endsWith[16];
const char mimeType[32];
};
extern const Entry mimeTable[maxType];
}
#endif

View File

@ -0,0 +1,66 @@
#ifndef URI_BRACES_H
#define URI_BRACES_H
#include "Uri.h"
class UriBraces : public Uri {
public:
explicit UriBraces(const char *uri) : Uri(uri) {};
explicit UriBraces(const String &uri) : Uri(uri) {};
Uri* clone() const override final {
return new UriBraces(_uri);
};
void initPathArgs(std::vector<String> &pathArgs) override final {
int numParams = 0, start = 0;
do {
start = _uri.indexOf("{}", start);
if (start > 0) {
numParams++;
start += 2;
}
} while (start > 0);
pathArgs.resize(numParams);
}
bool canHandle(const String &requestUri, std::vector<String> &pathArgs) override final {
if (Uri::canHandle(requestUri, pathArgs))
return true;
size_t uriLength = _uri.length();
unsigned int pathArgIndex = 0;
unsigned int requestUriIndex = 0;
for (unsigned int i = 0; i < uriLength; i++, requestUriIndex++) {
char uriChar = _uri[i];
char requestUriChar = requestUri[requestUriIndex];
if (uriChar == requestUriChar)
continue;
if (uriChar != '{')
return false;
i += 2; // index of char after '}'
if (i >= uriLength) {
// there is no char after '}'
pathArgs[pathArgIndex] = requestUri.substring(requestUriIndex);
return pathArgs[pathArgIndex].indexOf("/") == -1; // path argument may not contain a '/'
}
else
{
char charEnd = _uri[i];
int uriIndex = requestUri.indexOf(charEnd, requestUriIndex);
if (uriIndex < 0)
return false;
pathArgs[pathArgIndex] = requestUri.substring(requestUriIndex, uriIndex);
requestUriIndex = (unsigned int) uriIndex;
}
pathArgIndex++;
}
return requestUriIndex >= requestUri.length();
}
};
#endif

View File

@ -0,0 +1,22 @@
#ifndef URI_GLOB_H
#define URI_GLOB_H
#include "Uri.h"
#include <fnmatch.h>
class UriGlob : public Uri {
public:
explicit UriGlob(const char *uri) : Uri(uri) {};
explicit UriGlob(const String &uri) : Uri(uri) {};
Uri* clone() const override final {
return new UriGlob(_uri);
};
bool canHandle(const String &requestUri, __attribute__((unused)) std::vector<String> &pathArgs) override final {
return fnmatch(_uri.c_str(), requestUri.c_str(), 0) == 0;
}
};
#endif

View File

@ -0,0 +1,44 @@
#ifndef URI_REGEX_H
#define URI_REGEX_H
#include "Uri.h"
#include <regex>
class UriRegex : public Uri {
public:
explicit UriRegex(const char *uri) : Uri(uri) {};
explicit UriRegex(const String &uri) : Uri(uri) {};
Uri* clone() const override final {
return new UriRegex(_uri);
};
void initPathArgs(std::vector<String> &pathArgs) override final {
std::regex rgx((_uri + "|").c_str());
std::smatch matches;
std::string s{""};
std::regex_search(s, matches, rgx);
pathArgs.resize(matches.size() - 1);
}
bool canHandle(const String &requestUri, std::vector<String> &pathArgs) override final {
if (Uri::canHandle(requestUri, pathArgs))
return true;
unsigned int pathArgIndex = 0;
std::regex rgx(_uri.c_str());
std::smatch matches;
std::string s(requestUri.c_str());
if (std::regex_search(s, matches, rgx)) {
for (size_t i = 1; i < matches.size(); ++i) { // skip first
pathArgs[pathArgIndex] = String(matches[i].str().c_str());
pathArgIndex++;
}
return true;
}
return false;
}
};
#endif