diff --git a/CHANGELOG.md b/CHANGELOG.md index 95ede8f9f..b5b0fe317 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ All notable changes to this project will be documented in this file. - Provide serial upload port from VSC to PIO (#23436) - Berry support for `sortedmap` (#23441) - Berry `introspect.module` option to not cache module entry +- Berry `webserver.remove_route` to revert `webserver.on` ### Breaking Changed diff --git a/lib/libesp32/berry_tasmota/src/be_webserver_lib.c b/lib/libesp32/berry_tasmota/src/be_webserver_lib.c index 34caf01af..c217e9e67 100644 --- a/lib/libesp32/berry_tasmota/src/be_webserver_lib.c +++ b/lib/libesp32/berry_tasmota/src/be_webserver_lib.c @@ -14,6 +14,7 @@ extern int w_webserver_member(bvm *vm); extern int w_webserver_on(bvm *vm); +extern int w_webserver_remove_route(bvm *vm); extern int w_webserver_state(bvm *vm); extern int w_webserver_check_privileged_access(bvm *vm); @@ -95,11 +96,24 @@ static const berry_webserver_cb_t berry_callback_array[WEBSERVER_REQ_HANDLER_HOO // -1 if no more available berry_webserver_cb_t be_webserver_allocate_hook(bvm *vm, int32_t slot, bvalue *f) { if (slot < 0 || slot >= WEBSERVER_REQ_HANDLER_HOOK_MAX) return NULL; // invalid call, avoid a crash + if (be_isgcobj(f)) { + be_gc_fix_set(vm, f->v.gc, btrue); // mark the function as non-gc + } be_webserver_cb_hooks[slot].vm = vm; be_webserver_cb_hooks[slot].f = *f; return berry_callback_array[slot]; } +bbool be_webserver_deallocate_hook(bvm *vm, int32_t slot) { + if (slot < 0 || slot >= WEBSERVER_REQ_HANDLER_HOOK_MAX) return bfalse; // invalid call, avoid a crash + bvalue f = be_webserver_cb_hooks[slot].f; + if (be_isgcobj(&f)) { + be_gc_fix_set(vm, f.v.gc, bfalse); // remove the marker for non-gc + } + var_setnil(&be_webserver_cb_hooks[slot].f); + return btrue; +} + /*********************************************************************************************\ * `be_webserver_cb_deinit`: * Clean any callback for this VM @@ -144,6 +158,7 @@ module webserver (scope: global) { member, func(w_webserver_member) on, func(w_webserver_on) + remove_route, func(w_webserver_remove_route) state, func(w_webserver_state) check_privileged_access, func(w_webserver_check_privileged_access) diff --git a/tasmota/tasmota_xdrv_driver/xdrv_01_9_webserver.ino b/tasmota/tasmota_xdrv_driver/xdrv_01_9_webserver.ino index 76b8c8e30..f0e595aac 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_01_9_webserver.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_01_9_webserver.ino @@ -646,6 +646,13 @@ void WebServer_on(const char * prefix, void (*func)(void), uint8_t method = HTTP #endif // ESP32 } +#ifdef ESP32 +void WebServer_removeRoute(const char * prefix, uint8_t method = HTTP_ANY) { + if (Webserver == nullptr) { return; } + Webserver->removeRoute(prefix, (HTTPMethod) method); +} +#endif // ESP32 + /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ // Always listens to all interfaces, so we don't need an IP address anymore diff --git a/tasmota/tasmota_xdrv_driver/xdrv_52_3_berry_webserver.ino b/tasmota/tasmota_xdrv_driver/xdrv_52_3_berry_webserver.ino index dc2c75420..491af7aa9 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_52_3_berry_webserver.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_52_3_berry_webserver.ino @@ -78,7 +78,8 @@ static uint8_t be_webserver_method[WEBSERVER_REQ_HANDLER_HOOK_MAX]; extern "C" { typedef void (*berry_webserver_cb_t)(void); extern berry_webserver_cb_t be_webserver_allocate_hook(bvm *vm, int32_t num, bvalue *f); - // Berry: `webserver.on(prefix:string, callback:closure) -> nil` + extern bbool be_webserver_deallocate_hook(bvm *vm, int32_t slot); + // Berry: `webserver.on(prefix:string, callback:closure [, method:int]) -> nil` // // WARNING - this should be called only when receiving `web_add_handler` event. // If called before the WebServer is set up and Wifi on, it will crash. @@ -119,9 +120,6 @@ extern "C" { // AddLog(LOG_LEVEL_INFO, ">>>: slot found = %i", slot); bvalue *v = be_indexof(vm, 2); - if (be_isgcobj(v)) { - be_gc_fix_set(vm, v->v.gc, btrue); // mark the function as non-gc - } berry_webserver_cb_t cb = be_webserver_allocate_hook(vm, slot, v); if (cb == NULL) { be_raise(vm, kInternalError, nullptr); } be_webserver_prefix[slot] = prefix; @@ -133,6 +131,45 @@ extern "C" { be_raise(vm, kTypeError, nullptr); } + // Berry: `webserver.remove_handler(prefix:string [, method:int]) -> nil` + // + // Remove a handler already added with `webserver.on()` + // Does nothing if the handler does not exist or was already removed + // + int32_t w_webserver_remove_route(struct bvm *vm); + int32_t w_webserver_remove_route(struct bvm *vm) { + int32_t argc = be_top(vm); // Get the number of arguments + bbool good = bfalse; + if (argc >= 1 && be_isstring(vm, 1) && (argc < 2 || be_isint(vm, 2)) ) { // optional second argument must be int + uint8_t method = HTTP_ANY; // default method if not specified + const char * prefix = be_tostring(vm, 1); + if (argc >= 2) { + method = be_toint(vm, 2); + } + + // find the slot that matches the prefix/method + int32_t slot; + for (slot = 0; slot < WEBSERVER_REQ_HANDLER_HOOK_MAX; slot++) { + if (be_webserver_prefix[slot] == prefix && be_webserver_method[slot] == method) { + break; + } + } + + if (slot < WEBSERVER_REQ_HANDLER_HOOK_MAX) { + // remove from handler + WebServer_removeRoute(be_webserver_prefix[slot].c_str(), be_webserver_method[slot]); + // mark slot as free + be_webserver_prefix[slot] = ""; + // Deallocate cb slot + be_webserver_deallocate_hook(vm, slot); + good = btrue; + } + be_pushbool(vm, good); + be_return(vm); // return, all good + } + be_raise(vm, kTypeError, nullptr); + } + // Berry: `webserver.state() -> int` // int32_t w_webserver_state(struct bvm *vm);