diff --git a/examples/URIMatcherTest/URIMatcherTest.ino b/examples/URIMatcherTest/URIMatcherTest.ino new file mode 100644 index 00000000..159d50c4 --- /dev/null +++ b/examples/URIMatcherTest/URIMatcherTest.ino @@ -0,0 +1,89 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// Copyright 2016-2025 Hristo Gochkov, Mathieu Carbou, Emil Muratov + +// +// Test for ESPAsyncWebServer URI matching +// +// Usage: upload, connect to the AP and run test_routes.sh +// + +#include +#if defined(ESP32) || defined(LIBRETINY) +#include +#include +#elif defined(ESP8266) +#include +#include +#elif defined(TARGET_RP2040) || defined(TARGET_RP2350) || defined(PICO_RP2040) || defined(PICO_RP2350) +#include +#include +#endif + +#include + +AsyncWebServer server(80); + +void setup() { + Serial.begin(115200); + +#if SOC_WIFI_SUPPORTED || CONFIG_ESP_WIFI_REMOTE_ENABLED || LT_ARD_HAS_WIFI + WiFi.mode(WIFI_AP); + WiFi.softAP("esp-captive"); +#endif + + // Status endpoint + server.on("/status", HTTP_GET, [](AsyncWebServerRequest *request) { + request->send(200, "text/plain", "OK"); + }); + + // Exact paths, plus the subpath (/exact matches /exact/sub but not /exact-no-match) + server.on("/exact", HTTP_GET, [](AsyncWebServerRequest *request) { + request->send(200, "text/plain", "OK"); + }); + + server.on("/api/users", HTTP_GET, [](AsyncWebServerRequest *request) { + request->send(200, "text/plain", "OK"); + }); + + // Prefix matching + server.on("/api/*", HTTP_GET, [](AsyncWebServerRequest *request) { + request->send(200, "text/plain", "OK"); + }); + + server.on("/files/*", HTTP_GET, [](AsyncWebServerRequest *request) { + request->send(200, "text/plain", "OK"); + }); + + // Extensions + server.on("/*.json", HTTP_GET, [](AsyncWebServerRequest *request) { + request->send(200, "application/json", "{\"status\":\"OK\"}"); + }); + + server.on("/*.css", HTTP_GET, [](AsyncWebServerRequest *request) { + request->send(200, "text/css", "/* OK */"); + }); + +#ifdef ASYNCWEBSERVER_REGEX + // Regex patterns + server.on("^/user/([0-9]+)$", HTTP_GET, [](AsyncWebServerRequest *request) { + request->send(200, "text/plain", "OK"); + }); + + server.on("^/blog/([0-9]{4})/([0-9]{2})/([0-9]{2})$", HTTP_GET, [](AsyncWebServerRequest *request) { + request->send(200, "text/plain", "OK"); + }); +#endif + + // 404 handler + server.onNotFound([](AsyncWebServerRequest *request) { + request->send(404, "text/plain", "Not Found"); + }); + + server.begin(); + Serial.println("Server ready"); +} + +// not needed +void loop() { + delay(100); +} diff --git a/examples/URIMatcherTest/test_routes.sh b/examples/URIMatcherTest/test_routes.sh new file mode 100755 index 00000000..2c3fcad3 --- /dev/null +++ b/examples/URIMatcherTest/test_routes.sh @@ -0,0 +1,91 @@ +#!/bin/bash + +# URI Matcher Test Script +# Tests all routes defined in URIMatcherTest.ino + +SERVER_IP="${1:-192.168.4.1}" +SERVER_PORT="80" +BASE_URL="http://${SERVER_IP}:${SERVER_PORT}" + +echo "Testing URI Matcher at $BASE_URL" +echo "==================================" + +# Function to test a route +test_route() { + local path="$1" + local expected_status="$2" + local description="$3" + + echo -n "Testing $path ... " + + response=$(curl -s -w "HTTPSTATUS:%{http_code}" "$BASE_URL$path" 2>/dev/null) + status_code=$(echo "$response" | grep -o "HTTPSTATUS:[0-9]*" | cut -d: -f2) + + if [ "$status_code" = "$expected_status" ]; then + echo "✅ PASS ($status_code)" + else + echo "❌ FAIL (expected $expected_status, got $status_code)" + return 1 + fi + return 0 +} + +# Test counter +PASS=0 +FAIL=0 + +# Test all routes that should return 200 OK +echo "Testing routes that should work (200 OK):" + +if test_route "/status" "200" "Status endpoint"; then ((PASS++)); else ((FAIL++)); fi +if test_route "/exact" "200" "Exact path"; then ((PASS++)); else ((FAIL++)); fi +if test_route "/exact/" "200" "Exact path ending with /"; then ((PASS++)); else ((FAIL++)); fi +if test_route "/exact/sub" "200" "Exact path with subpath /"; then ((PASS++)); else ((FAIL++)); fi +if test_route "/api/users" "200" "Exact API path"; then ((PASS++)); else ((FAIL++)); fi +if test_route "/api/data" "200" "API prefix match"; then ((PASS++)); else ((FAIL++)); fi +if test_route "/api/v1/posts" "200" "API prefix deep"; then ((PASS++)); else ((FAIL++)); fi +if test_route "/files/document.pdf" "200" "Files prefix"; then ((PASS++)); else ((FAIL++)); fi +if test_route "/files/images/photo.jpg" "200" "Files prefix deep"; then ((PASS++)); else ((FAIL++)); fi +if test_route "/config.json" "200" "JSON extension"; then ((PASS++)); else ((FAIL++)); fi +if test_route "/data/settings.json" "200" "JSON extension in subfolder"; then ((PASS++)); else ((FAIL++)); fi +if test_route "/style.css" "200" "CSS extension"; then ((PASS++)); else ((FAIL++)); fi +if test_route "/assets/main.css" "200" "CSS extension in subfolder"; then ((PASS++)); else ((FAIL++)); fi + +# Check if regex is enabled by testing the server +echo "" +echo "Checking for regex support..." +regex_test=$(curl -s "$BASE_URL/user/123" 2>/dev/null) +if curl -s -w "%{http_code}" "$BASE_URL/user/123" 2>/dev/null | grep -q "200"; then + echo "Regex support detected - testing regex routes:" + if test_route "/user/123" "200" "Regex user ID"; then ((PASS++)); else ((FAIL++)); fi + if test_route "/user/456" "200" "Regex user ID 2"; then ((PASS++)); else ((FAIL++)); fi + if test_route "/blog/2023/10/15" "200" "Regex blog date"; then ((PASS++)); else ((FAIL++)); fi + if test_route "/blog/2024/12/25" "200" "Regex blog date 2"; then ((PASS++)); else ((FAIL++)); fi +else + echo "Regex support not detected (compile with ASYNCWEBSERVER_REGEX to enable)" +fi + +echo "" +echo "Testing routes that should fail (404 Not Found):" + +if test_route "/nonexistent" "404" "Non-existent route"; then ((PASS++)); else ((FAIL++)); fi +if test_route "/exact-sub" "404" "Exact path with extra characters"; then ((PASS++)); else ((FAIL++)); fi +if test_route "/user/abc" "404" "Invalid regex (letters instead of numbers)"; then ((PASS++)); else ((FAIL++)); fi +if test_route "/blog/23/10/15" "404" "Invalid regex (2-digit year)"; then ((PASS++)); else ((FAIL++)); fi + +echo "" +echo "==================================" +echo "Test Results:" +echo "✅ Passed: $PASS" +echo "❌ Failed: $FAIL" +echo "Total: $((PASS + FAIL))" + +if [ $FAIL -eq 0 ]; then + echo "" + echo "🎉 All tests passed! URI matching is working correctly." + exit 0 +else + echo "" + echo "❌ Some tests failed. Check the server and routes." + exit 1 +fi diff --git a/platformio.ini b/platformio.ini index 380f859d..c507f321 100644 --- a/platformio.ini +++ b/platformio.ini @@ -33,6 +33,7 @@ src_dir = examples/PerfTests ; src_dir = examples/StaticFile ; src_dir = examples/Templates ; src_dir = examples/Upload +; src_dir = examples/URIMatcherTest ; src_dir = examples/WebSocket ; src_dir = examples/WebSocketEasy