Skip to content

Commit

Permalink
Split SimpleServer in multiple examples
Browse files Browse the repository at this point in the history
  • Loading branch information
mathieucarbou committed Jan 30, 2025
1 parent a1e7550 commit 2a7451e
Show file tree
Hide file tree
Showing 33 changed files with 2,275 additions and 584 deletions.
14 changes: 14 additions & 0 deletions .github/scripts/list-examples.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@

#!/bin/bash
# shellcheck disable=SC2002

# fail the script if any command unexpectedly fails
set -e

examples=$(ls examples)
matrix="["
for example in $examples; do
matrix="$matrix\"$example\","
done
matrix="${matrix%,}]"
echo "$matrix"
80 changes: 46 additions & 34 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,49 @@ concurrency:
cancel-in-progress: true

jobs:
arduino:
name: Arduino
arduino-lint:
name: Arduino Lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: arduino/arduino-lint-action@v2
with:
library-manager: update

list-examples:
name: List examples
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- id: list-examples
run: |
examples=$(./list-examples.sh)
echo "::set-output name=examples::$examples"
arduino-setup:
name: Arduino Library Setup
runs-on: ubuntu-latest
steps:
- name: Install arduino-cli
run: curl -fsSL https://raw.githubusercontent.com/arduino/arduino-cli/master/install.sh | BINDIR=/usr/local/bin sh

- name: Update core index
run: arduino-cli core update-index --additional-urls "${{ matrix.index_url }}"

- name: Install core
run: arduino-cli core install --additional-urls "${{ matrix.index_url }}" ${{ matrix.core }}

- name: Install ArduinoJson
run: arduino-cli lib install ArduinoJson

arduino-build:
name: Arduino CLI Build
runs-on: ubuntu-latest
needs: [list-examples, arduino-setup]
strategy:
fail-fast: false
matrix:
example: ${{ fromJson(needs.build.outputs.examples) }}
include:
- core: esp32:esp32
board: esp32:esp32:esp32
Expand All @@ -39,23 +76,6 @@ jobs:
- name: Checkout
uses: actions/checkout@v4

- name: Arduino Lint
uses: arduino/arduino-lint-action@v2
with:
library-manager: update

- name: Install arduino-cli
run: curl -fsSL https://raw.githubusercontent.com/arduino/arduino-cli/master/install.sh | BINDIR=/usr/local/bin sh

- name: Update core index
run: arduino-cli core update-index --additional-urls "${{ matrix.index_url }}"

- name: Install core
run: arduino-cli core install --additional-urls "${{ matrix.index_url }}" ${{ matrix.core }}

- name: Install ArduinoJson
run: arduino-cli lib install ArduinoJson

- name: Install AsyncTCP (ESP32)
if: ${{ matrix.core == 'esp32:esp32' }}
run: ARDUINO_LIBRARY_ENABLE_UNSAFE_INSTALL=true arduino-cli lib install --git-url https://github.com/ESP32Async/AsyncTCP#v3.3.3
Expand All @@ -68,17 +88,8 @@ jobs:
if: ${{ matrix.core == 'rp2040:rp2040' }}
run: ARDUINO_LIBRARY_ENABLE_UNSAFE_INSTALL=true arduino-cli lib install --git-url https://github.com/khoih-prog/AsyncTCP_RP2040W#v1.2.0

- name: Build CaptivePortal
run: arduino-cli compile --library . --warnings none -b ${{ matrix.board }} "examples/CaptivePortal/CaptivePortal.ino"

- name: Build SimpleServer
run: arduino-cli compile --library . --warnings none -b ${{ matrix.board }} "examples/SimpleServer/SimpleServer.ino"

- name: Build Filters
run: arduino-cli compile --library . --warnings none -b ${{ matrix.board }} "examples/Filters/Filters.ino"

- name: Build StreamFiles
run: arduino-cli compile --library . --warnings none -b ${{ matrix.board }} "examples/StreamFiles/StreamFiles.ino"
- name: Build
run: arduino-cli compile --library . --warnings none -b ${{ matrix.board }} "examples/${{ matrix.example }}/${{ matrix.example }}.ino"

platformio:
name: "pio:${{ matrix.env }}:${{ matrix.board }}"
Expand Down Expand Up @@ -147,7 +158,8 @@ jobs:
python -m pip install --upgrade pip
pip install --upgrade platformio
- run: PLATFORMIO_SRC_DIR=examples/CaptivePortal PIO_BOARD=${{ matrix.board }} pio run -e ${{ matrix.env }}
- run: PLATFORMIO_SRC_DIR=examples/SimpleServer PIO_BOARD=${{ matrix.board }} pio run -e ${{ matrix.env }}
- run: PLATFORMIO_SRC_DIR=examples/Filters PIO_BOARD=${{ matrix.board }} pio run -e ${{ matrix.env }}
- run: PLATFORMIO_SRC_DIR=examples/StreamFiles PIO_BOARD=${{ matrix.board }} pio run -e ${{ matrix.env }}
- run: |
for i in `ls examples`; do
echo "Building examples/$i..."
PLATFORMIO_SRC_DIR=examples/$i PIO_BOARD=${{ matrix.board }} pio run -e ${{ matrix.env }}
done
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// Copyright 2016-2025 Hristo Gochkov, Mathieu Carbou, Emil Muratov

// A simple server implementation showing how to:
// * serve static messages
// * read GET and POST parameters
// * handle missing pages / 404s
//
// This is an All in one example ;-)
// Do not run this example: it just holds the code for compilation purposes
//

#include <Arduino.h>
#ifdef ESP32
Expand Down
155 changes: 155 additions & 0 deletions examples/Auth/Auth.ino
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// Copyright 2016-2025 Hristo Gochkov, Mathieu Carbou, Emil Muratov

//
// Authentication and authorization middlewares
//

#include <Arduino.h>
#ifdef ESP32
#include <AsyncTCP.h>
#include <WiFi.h>
#elif defined(ESP8266)
#include <ESP8266WiFi.h>
#include <ESPAsyncTCP.h>
#elif defined(TARGET_RP2040)
#include <WebServer.h>
#include <WiFi.h>
#endif

#include <ESPAsyncWebServer.h>

static AsyncWebServer server(80);

// basicAuth
static AsyncAuthenticationMiddleware basicAuth;
static AsyncAuthenticationMiddleware basicAuthHash;

// simple digest authentication
static AsyncAuthenticationMiddleware digestAuth;
static AsyncAuthenticationMiddleware digestAuthHash;

// complex authentication which adds request attributes for the next middlewares and handler
static AsyncMiddlewareFunction complexAuth([](AsyncWebServerRequest *request, ArMiddlewareNext next) {
if (!request->authenticate("user", "password")) {
return request->requestAuthentication();
}

// add attributes to the request for the next middlewares and handler
request->setAttribute("user", "Mathieu");
request->setAttribute("role", "staff");
if (request->hasParam("token")) {
request->setAttribute("token", request->getParam("token")->value().c_str());
}

next();
});

static AsyncAuthorizationMiddleware authz([](AsyncWebServerRequest *request) {
return request->getAttribute("token") == "123";
});

void setup() {
Serial.begin(115200);

#ifndef CONFIG_IDF_TARGET_ESP32H2
WiFi.mode(WIFI_AP);
WiFi.softAP("esp-captive");
#endif

// basic authentication
basicAuth.setUsername("admin");
basicAuth.setPassword("admin");
basicAuth.setRealm("MyApp");
basicAuth.setAuthFailureMessage("Authentication failed");
basicAuth.setAuthType(AsyncAuthType::AUTH_BASIC);
basicAuth.generateHash(); // precompute hash (optional but recommended)

// basic authentication with hash
basicAuthHash.setUsername("admin");
basicAuthHash.setPasswordHash("YWRtaW46YWRtaW4="); // BASE64(admin:admin)
basicAuthHash.setRealm("MyApp");
basicAuthHash.setAuthFailureMessage("Authentication failed");
basicAuthHash.setAuthType(AsyncAuthType::AUTH_BASIC);

// digest authentication
digestAuth.setUsername("admin");
digestAuth.setPassword("admin");
digestAuth.setRealm("MyApp");
digestAuth.setAuthFailureMessage("Authentication failed");
digestAuth.setAuthType(AsyncAuthType::AUTH_DIGEST);
digestAuth.generateHash(); // precompute hash (optional but recommended)

// digest authentication with hash
digestAuthHash.setUsername("admin");
digestAuthHash.setPasswordHash("f499b71f9a36d838b79268e145e132f7"); // MD5(user:realm:pass)
digestAuthHash.setRealm("MyApp");
digestAuthHash.setAuthFailureMessage("Authentication failed");
digestAuthHash.setAuthType(AsyncAuthType::AUTH_DIGEST);

// basic authentication method
// curl -v -u admin:admin http://192.168.4.1/auth-basic
server
.on(
"/auth-basic", HTTP_GET,
[](AsyncWebServerRequest *request) {
request->send(200, "text/plain", "Hello, world!");
}
)
.addMiddleware(&basicAuth);

// basic authentication method with hash
// curl -v -u admin:admin http://192.168.4.1/auth-basic-hash
server
.on(
"/auth-basic-hash", HTTP_GET,
[](AsyncWebServerRequest *request) {
request->send(200, "text/plain", "Hello, world!");
}
)
.addMiddleware(&basicAuthHash);

// digest authentication
// curl -v -u admin:admin --digest http://192.168.4.1/auth-digest
server
.on(
"/auth-digest", HTTP_GET,
[](AsyncWebServerRequest *request) {
request->send(200, "text/plain", "Hello, world!");
}
)
.addMiddleware(&digestAuth);

// digest authentication with hash
// curl -v -u admin:admin --digest http://192.168.4.1/auth-digest-hash
server
.on(
"/auth-digest-hash", HTTP_GET,
[](AsyncWebServerRequest *request) {
request->send(200, "text/plain", "Hello, world!");
}
)
.addMiddleware(&digestAuthHash);

// test digest auth custom authorization middleware
// curl -v --digest -u user:password http://192.168.4.1/auth-custom?token=123 => OK
// curl -v --digest -u user:password http://192.168.4.1/auth-custom?token=456 => 403
// curl -v --digest -u user:FAILED http://192.168.4.1/auth-custom?token=456 => 401
server
.on(
"/auth-custom", HTTP_GET,
[](AsyncWebServerRequest *request) {
String buffer = "Hello ";
buffer.concat(request->getAttribute("user"));
buffer.concat(" with role: ");
buffer.concat(request->getAttribute("role"));
request->send(200, "text/plain", buffer);
}
)
.addMiddlewares({&complexAuth, &authz});

server.begin();
}

// not needed
void loop() {}
58 changes: 58 additions & 0 deletions examples/CORS/CORS.ino
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// Copyright 2016-2025 Hristo Gochkov, Mathieu Carbou, Emil Muratov

//
// How to use CORS middleware
//

#include <Arduino.h>
#ifdef ESP32
#include <AsyncTCP.h>
#include <WiFi.h>
#elif defined(ESP8266)
#include <ESP8266WiFi.h>
#include <ESPAsyncTCP.h>
#elif defined(TARGET_RP2040)
#include <WebServer.h>
#include <WiFi.h>
#endif

#include <ESPAsyncWebServer.h>

static AsyncWebServer server(80);
static AsyncCorsMiddleware cors;

void setup() {
Serial.begin(115200);

#ifndef CONFIG_IDF_TARGET_ESP32H2
WiFi.mode(WIFI_AP);
WiFi.softAP("esp-captive");
#endif

cors.setOrigin("http://192.168.4.1");
cors.setMethods("POST, GET, OPTIONS, DELETE");
cors.setHeaders("X-Custom-Header");
cors.setAllowCredentials(false);
cors.setMaxAge(600);

server.addMiddleware(&cors);

// Test CORS preflight request
// curl -v -X OPTIONS -H "origin: http://192.168.4.1" http://192.168.4.1/cors
//
// Test CORS request
// curl -v -H "origin: http://192.168.4.1" http://192.168.4.1/cors
//
// Test non-CORS request
// curl -v http://192.168.4.1/cors
//
server.on("/cors", HTTP_GET, [](AsyncWebServerRequest *request) {
request->send(200, "text/plain", "Hello, world!");
});

server.begin();
}

// not needed
void loop() {}
4 changes: 2 additions & 2 deletions examples/CaptivePortal/CaptivePortal.ino
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@
#endif
#include "ESPAsyncWebServer.h"

DNSServer dnsServer;
AsyncWebServer server(80);
static DNSServer dnsServer;
static AsyncWebServer server(80);

class CaptiveRequestHandler : public AsyncWebHandler {
public:
Expand Down
Loading

0 comments on commit 2a7451e

Please sign in to comment.