Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 70 additions & 0 deletions examples/Router/router.ino
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
//
// Shows how to create routers
//

#include <Arduino.h>
#ifdef ESP32
#include <AsyncTCP.h>
#include <WiFi.h>
#elif defined(ESP8266)
#include <ESP8266WiFi.h>
#include <ESPAsyncTCP.h>
#elif defined(TARGET_RP2040) || defined(TARGET_RP2350) || defined(PICO_RP2040) || defined(PICO_RP2350)
#include <RPAsyncTCP.h>
#include <WiFi.h>
#endif

#include <ESPAsyncWebServer.h>

static AsyncWebServer server(80);
static AsyncRouter settingsRouter(&server, "/settings");
static AsyncRouter exampleRouter(&server, "/example");

void timeSettingController(AsyncWebServerRequest *request) {
request->send(200, "text/plain", "Hello from /settings/time");
}

void otherSettingController(AsyncWebServerRequest *request) {
request->send(200, "text/plain", "Hello from /settings/other");
}

void exampleController(AsyncWebServerRequest *request) {
request->send(200, "text/plain", "Hello from /example/test");
}

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

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

// Create the routes and assign controller functions to them
settingsRouter.on("/time", timeSettingController);
settingsRouter.on("/other", otherSettingController);

// An other example
exampleRouter.on("/test", exampleController);

// Inline controller function
exampleRouter.on("/test2", [](AsyncWebServerRequest *request){
request->send(200, "text/plain", "Hello from /example/test2");
});

// With specific method
exampleRouter.on("/test2", HTTP_GET, [](AsyncWebServerRequest *request){
request->send(200, "text/plain", "Hello from /example/test2");
});

// Add the routers to the server
server.addRouter(&settingsRouter);
server.addRouter(&exampleRouter);

server.begin();
}

// not needed
void loop() {
delay(100);
}
43 changes: 43 additions & 0 deletions src/AsyncRouter.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
#include "AsyncRouter.h"

AsyncRouter::AsyncRouter(AsyncWebServer* server, const char* path) {
_server = server;
_path = path;
}

AsyncRouter::~AsyncRouter() {
for (AsyncCallbackWebHandler* handler : _handlers) {
_server->removeHandler(handler);
delete handler;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the handler is a static class instance created by the user ?

}
}

AsyncCallbackWebHandler& AsyncRouter::on(const char* uri, WebRequestMethodComposite method, ArRequestHandlerFunction onRequest, ArUploadHandlerFunction onUpload, ArBodyHandlerFunction onBody) {
AsyncCallbackWebHandler& handler = _server->on(
(_path + uri).c_str(),
method,
onRequest,
onUpload,
onBody
);
_handlers.push_back(&handler);
return handler;
};

AsyncCallbackWebHandler& AsyncRouter::on(const char* uri, ArRequestHandlerFunction onRequest) {
return _server->on((_path + uri).c_str(), HTTP_ANY, onRequest);
}

AsyncCallbackWebHandler& AsyncRouter::on(const char* uri, WebRequestMethod method, ArRequestHandlerFunction onRequest) {
return _server->on((_path + uri).c_str(), method, onRequest);
}

void AsyncRouter::addMidleware(AsyncMiddleware* middleware) {
for (AsyncCallbackWebHandler* handler : _handlers) {
handler->addMiddleware(middleware);
}
}

std::string AsyncRouter::path() {
return _path;
}
38 changes: 38 additions & 0 deletions src/AsyncRouter.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
#ifndef ASYNC_ROUTER_H_
#define ASYNC_ROUTER_H_

#include <Arduino.h>
#include <ESPAsyncWebServer.h>
#include <string>

class AsyncMiddleware;

typedef std::function<void(AsyncWebServerRequest *request)> ArRequestHandlerFunction;
typedef std::function<void(AsyncWebServerRequest *request, const String &filename, size_t index, uint8_t *data, size_t len, bool final)>
ArUploadHandlerFunction;
typedef std::function<void(AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total)> ArBodyHandlerFunction;
typedef uint8_t WebRequestMethodComposite;

class AsyncRouter {
public:
AsyncRouter(AsyncWebServer* server, const char *path);
~AsyncRouter();
AsyncCallbackWebHandler& on(
const char* uri,
WebRequestMethodComposite method,
ArRequestHandlerFunction onRequest,
ArUploadHandlerFunction onUpload = nullptr,
ArBodyHandlerFunction onBody = nullptr
);
AsyncCallbackWebHandler& on(const char* uri, ArRequestHandlerFunction onRequest);
AsyncCallbackWebHandler& on(const char* uri, WebRequestMethod method, ArRequestHandlerFunction onRequest);

void addMidleware(AsyncMiddleware* middleware);
std::string path();
private:
std::string _path;
Comment thread
zekageri marked this conversation as resolved.
AsyncWebServer* _server;
std::list<AsyncCallbackWebHandler*> _handlers;
};

#endif
34 changes: 34 additions & 0 deletions src/ESPAsyncWebServer.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ class AsyncStaticWebHandler;
class AsyncCallbackWebHandler;
class AsyncResponseStream;
class AsyncMiddlewareChain;
class AsyncRouter;

#if defined(TARGET_RP2040) || defined(TARGET_RP2350) || defined(PICO_RP2040) || defined(PICO_RP2350)
typedef enum http_method WebRequestMethod;
Expand Down Expand Up @@ -88,6 +89,7 @@ class FileOpenMode {
#else
#include "FileOpenMode.h"
#endif
#include "AsyncRouter.h"

// if this value is returned when asked for data, packet will not be sent and you will be asked for data again
#define RESPONSE_TRY_AGAIN 0xFFFFFFFF
Expand Down Expand Up @@ -1090,6 +1092,7 @@ class AsyncWebServer : public AsyncMiddlewareChain {
AsyncServer _server;
std::list<std::shared_ptr<AsyncWebRewrite>> _rewrites;
std::list<std::unique_ptr<AsyncWebHandler>> _handlers;
std::list<std::unique_ptr<AsyncRouter>> _routers;
AsyncCallbackWebHandler *_catchAllHandler;

public:
Expand All @@ -1114,6 +1117,7 @@ class AsyncWebServer : public AsyncMiddlewareChain {
#endif

AsyncWebRewrite &addRewrite(AsyncWebRewrite *rewrite);
AsyncRouter &addRouter(AsyncRouter *router);
Comment thread
zekageri marked this conversation as resolved.

/**
* @brief (compat) Add url rewrite rule by pointer
Expand All @@ -1125,6 +1129,16 @@ class AsyncWebServer : public AsyncMiddlewareChain {
*/
AsyncWebRewrite &addRewrite(std::shared_ptr<AsyncWebRewrite> rewrite);

/**
* @brief (compat) Add router by pointer
* a deep copy of the pointer object will be created,
* it is up to user to manage further lifetime of the object in argument
*
* @param router pointer to router object to copy setting from
* @return AsyncRouter& reference to a newly created router
Comment on lines +1134 to +1138

Copilot AI May 11, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The documentation for addRouter(unique_ptr) mentions that a deep copy of the router object will be created, but the implementation instead transfers ownership. Please update the comment to accurately reflect the behavior.

Suggested change
* a deep copy of the pointer object will be created,
* it is up to user to manage further lifetime of the object in argument
*
* @param router pointer to router object to copy setting from
* @return AsyncRouter& reference to a newly created router
* Transfers ownership of the router object to the method.
* The caller is no longer responsible for managing the lifetime of the object.
*
* @param router unique pointer to the router object to be added
* @return AsyncRouter& reference to the added router

Copilot uses AI. Check for mistakes.
*/
AsyncRouter &addRouter(std::unique_ptr<AsyncRouter> router);

/**
* @brief add url rewrite rule
*
Expand All @@ -1144,6 +1158,7 @@ class AsyncWebServer : public AsyncMiddlewareChain {
*/
bool removeRewrite(AsyncWebRewrite *rewrite);


/**
* @brief remove rewrite rule
*
Expand All @@ -1154,6 +1169,25 @@ class AsyncWebServer : public AsyncMiddlewareChain {
*/
bool removeRewrite(const char *from, const char *to);

/**
* @brief (compat) remove router via referenced object
* this will NOT deallocate pointed object itself, internal router with same path will be removed if any
* it's a compat method, better use `removeRouter(const char* path)`
Comment on lines +1174 to +1175

Copilot AI May 11, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment states that removeRouter(AsyncRouter* router) will NOT deallocate the pointed object, yet the implementation removes the unique_ptr from the container (which will deallocate the object). Clarify the intended ownership and deallocation behavior in the documentation.

Suggested change
* this will NOT deallocate pointed object itself, internal router with same path will be removed if any
* it's a compat method, better use `removeRouter(const char* path)`
* this will deallocate the pointed object, as the internal container uses unique_ptr for ownership.
* It's a compat method, better use `removeRouter(const char* path)`

Copilot uses AI. Check for mistakes.
* @param router
* @return true
* @return false
*/
bool removeRouter(AsyncRouter *router);

/**
* @brief remove router by path
*
* @param path
* @return true
* @return false
*/
bool removeRouter(const char *path);

AsyncWebHandler &addHandler(AsyncWebHandler *handler);
bool removeHandler(AsyncWebHandler *handler);

Expand Down
24 changes: 24 additions & 0 deletions src/WebServer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,16 +53,40 @@ AsyncWebServer::~AsyncWebServer() {
_catchAllHandler = nullptr; // Prevent potential use-after-free
}

AsyncRouter &AsyncWebServer::addRouter(std::unique_ptr<AsyncRouter> router) {
_routers.emplace_back(std::move(router));
return *_routers.back().get();
}

AsyncWebRewrite &AsyncWebServer::addRewrite(std::shared_ptr<AsyncWebRewrite> rewrite) {
_rewrites.emplace_back(rewrite);
return *_rewrites.back().get();
}

AsyncRouter &AsyncWebServer::addRouter(AsyncRouter *router) {
_routers.emplace_back(router);
return *_routers.back().get();
}

AsyncWebRewrite &AsyncWebServer::addRewrite(AsyncWebRewrite *rewrite) {
_rewrites.emplace_back(rewrite);
return *_rewrites.back().get();
}

bool AsyncWebServer::removeRouter(AsyncRouter *router) {
return removeRouter(router->path().c_str());
}

bool AsyncWebServer::removeRouter(const char *path) {
for (auto r = _routers.begin(); r != _routers.end(); ++r) {
if (r->get()->path() == path) {
_routers.erase(r);
return true;
}
}
return false;
}

bool AsyncWebServer::removeRewrite(AsyncWebRewrite *rewrite) {
return removeRewrite(rewrite->from().c_str(), rewrite->toUrl().c_str());
}
Expand Down
Loading