diff --git a/.github/workflows/pre_release.yml b/.github/workflows/pre_release.yml index ca1fac59b..f5884632b 100644 --- a/.github/workflows/pre_release.yml +++ b/.github/workflows/pre_release.yml @@ -50,6 +50,8 @@ jobs: - name: Build all PIO target environments from default_envs run: | platformio run + env: + NO_BUILD_WEBUI: true - name: Create GitHub Release id: 'automatic_releases' diff --git a/.github/workflows/tagged_release.yml b/.github/workflows/tagged_release.yml index 2f3916aa6..aa5bd46d5 100644 --- a/.github/workflows/tagged_release.yml +++ b/.github/workflows/tagged_release.yml @@ -44,6 +44,8 @@ jobs: - name: Build all PIO target environments from default_envs run: | platformio run + env: + NO_BUILD_WEBUI: true - name: Create GitHub Release uses: emsesp/action-automatic-releases@v1.0.0 diff --git a/.github/workflows/test_release.yml b/.github/workflows/test_release.yml index 96ed83ddf..bfd34f80c 100644 --- a/.github/workflows/test_release.yml +++ b/.github/workflows/test_release.yml @@ -12,24 +12,30 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 + - name: Enable Corepack run: corepack enable + - uses: actions/setup-python@v5 with: python-version: '3.11' + - name: Use Node.js 20.x uses: actions/setup-node@v4 with: node-version: '20.x' + - name: Get EMS-ESP source code and version id: build_info run: | version=`grep -E '^#define EMSESP_APP_VERSION' ./src/version.h | awk -F'"' '{print $2}'` echo "VERSION=$version" >> $GITHUB_OUTPUT + - name: Install PlatformIO run: | python -m pip install --upgrade pip pip install -U platformio + - name: Build WebUI run: | cd interface @@ -38,9 +44,13 @@ jobs: sed -i "s/= 'pl'/= 'en'/" ./src/i18n/i18n-util.ts yarn build yarn webUI + - name: Build all target environments from default_envs run: | platformio run + env: + NO_BUILD_WEBUI: true + - name: Create GitHub Release id: 'automatic_releases' uses: emsesp/action-automatic-releases@v1.0.0 @@ -52,3 +62,4 @@ jobs: files: | CHANGELOG_LATEST.md ./build/firmware/*.* + diff --git a/.gitignore b/.gitignore index 3eaaae489..994fd23aa 100644 --- a/.gitignore +++ b/.gitignore @@ -12,17 +12,15 @@ cppcheck.out.xml # platformio .pio pio_local.ini -*_old # OS specific .DS_Store *Thumbs.db -# web specfic +# web specific build/ dist/ /data/www -/lib/framework/WWWData.h /interface/build node_modules /interface/.eslintcache @@ -35,6 +33,7 @@ stats.html analyse.html interface/vite.config.ts.timestamp* *.local +src/ESP32React/WWWData.h # i18n generated files interface/src/i18n/i18n-react.tsx @@ -66,3 +65,12 @@ words-found-verbose.txt # sonarlint compile_commands.json + +# pioarduino + hybrid +managed_components +dependencies.lock +CMakeLists.txt +.dummy/* +logs/* +sdkconfig.* +sdkconfig_tasmota_esp32 diff --git a/Makefile b/Makefile index ea61bda61..a8c45f781 100644 --- a/Makefile +++ b/Makefile @@ -40,26 +40,20 @@ MAKEFLAGS += -j $(JOBS) -l $(JOBS) # INCLUDES is a list of directories containing header files # LIBRARIES is a list of directories containing libraries, this must be the top level containing include and lib #---------------------------------------------------------------------- - -#TARGET := $(notdir $(CURDIR)) TARGET := emsesp BUILD := build -SOURCES := src src/* lib_standalone lib/uuid-common/src lib/uuid-console/src lib/uuid-log/src src/devices lib/ArduinoJson/src lib/PButton lib/semver lib/espMqttClient/src lib/espMqttClient/src/* -INCLUDES := src lib_standalone lib/espMqttClient/src lib/espMqttClient/src/Transport lib/ArduinoJson/src lib/uuid-common/src lib/uuid-console/src lib/uuid-log/src lib/uuid-telnet/src lib/uuid-syslog/src lib/semver lib/* src/devices +SOURCES := src/core src/devices src/web src/test lib_standalone lib/semver lib/espMqttClient/src lib/espMqttClient/src/* lib/ArduinoJson/src lib/uuid-common/src lib/uuid-console/src lib/uuid-log/src lib/PButton +INCLUDES := src/core src/devices src/web src/test lib/* lib_standalone lib/semver lib/espMqttClient/src lib/espMqttClient/src/Transport lib/ArduinoJson/src lib/uuid-common/src lib/uuid-console/src lib/uuid-log/src lib/uuid-telnet/src lib/uuid-syslog/src LIBRARIES := CPPCHECK = cppcheck -# CHECKFLAGS = -q --force --std=c++17 -CHECKFLAGS = -q --force --std=c++11 +CHECKFLAGS = -q --force --std=gnu++17 #---------------------------------------------------------------------- # Languages Standard #---------------------------------------------------------------------- C_STANDARD := -std=c17 -CXX_STANDARD := -std=gnu++14 - -# C_STANDARD := -std=c11 -# CXX_STANDARD := -std=c++11 +CXX_STANDARD := -std=gnu++17 #---------------------------------------------------------------------- # Defined Symbols @@ -68,7 +62,7 @@ DEFINES += -DARDUINOJSON_ENABLE -DARDUINOJSON_ENABLE_ARDUINO_STRING -DARDUINOJSO DEFINES += -DEMSESP_STANDALONE -DEMSESP_TEST -DEMSESP_DEBUG -DEMC_RX_BUFFER_SIZE=1500 DEFINES += $(ARGS) -DEFAULTS = -DEMSESP_DEFAULT_LOCALE=\"en\" -DEMSESP_DEFAULT_TX_MODE=8 -DEMSESP_DEFAULT_VERSION=\"3.7.1-dev\" -DEMSESP_DEFAULT_BOARD_PROFILE=\"S3\" +DEFAULTS = -DEMSESP_DEFAULT_LOCALE=\"en\" -DEMSESP_DEFAULT_TX_MODE=8 -DEMSESP_DEFAULT_VERSION=\"3.7.2-dev\" -DEMSESP_DEFAULT_BOARD_PROFILE=\"S3\" #---------------------------------------------------------------------- # Sources & Files @@ -108,7 +102,7 @@ CPPFLAGS += -Os CFLAGS += $(CPPFLAGS) CFLAGS += -Wall -Wextra -Werror -Wswitch-enum -CFLAGS += -Wno-tautological-constant-out-of-range-compare -Wno-unused-parameter -Wno-inconsistent-missing-override -Wno-missing-braces -Wno-unused-lambda-capture -Wno-sign-compare +CFLAGS += -Wno-unused-parameter -Wno-missing-braces -Wno-sign-compare CXXFLAGS += $(CFLAGS) -MMD #---------------------------------------------------------------------- diff --git a/boards/c3_mini_4M.json b/boards/c3_mini_4M.json new file mode 100644 index 000000000..7f19d1662 --- /dev/null +++ b/boards/c3_mini_4M.json @@ -0,0 +1,44 @@ +{ + "build": { + "arduino": { + "ldscript": "esp32c3_out.ld" + }, + "core": "esp32", + "extra_flags": [ + "-DARDUINO_LOLIN_C3_MINI", + "-DARDUINO_USB_MODE=1", + "-DARDUINO_USB_CDC_ON_BOOT=1" + ], + "f_cpu": "160000000L", + "f_flash": "80000000L", + "flash_mode": "qio", + "hwids": [ + [ + "0X303A", + "0x1001" + ] + ], + "mcu": "esp32c3", + "variant": "lolin_c3_mini" + }, + "connectivity": [ + "wifi" + ], + "debug": { + "openocd_target": "esp32c3.cfg" + }, + "frameworks": [ + "arduino", + "espidf" + ], + "name": "WEMOS LOLIN C3 Mini", + "upload": { + "flash_size": "4MB", + "maximum_ram_size": 327680, + "maximum_size": 4194304, + "require_upload_port": true, + "speed": 460800 + }, + "url": "https://www.wemos.cc/en/latest/c3/c3_mini.html", + "vendor": "WEMOS" + } \ No newline at end of file diff --git a/boards/s2_4M_P.json b/boards/s2_4M_P.json new file mode 100644 index 000000000..349b065b1 --- /dev/null +++ b/boards/s2_4M_P.json @@ -0,0 +1,47 @@ +{ + "build": { + "arduino": { + "ldscript": "esp32s2_out.ld" + }, + "core": "esp32", + "extra_flags": [ + "-DBOARD_HAS_PSRAM", + "-DTASMOTA_SDK", + "-DARDUINO_USB_CDC_ON_BOOT=1", + "-DARDUINO_USB_MODE=0" + ], + "f_cpu": "240000000L", + "f_flash": "80000000L", + "flash_mode": "dio", + "hwids": [ + [ + "0X303A", + "0x80C2" + ] + ], + "mcu": "esp32s2", + "variant": "lolin_s2_mini" + }, + "connectivity": [ + "wifi" + ], + "debug": { + "openocd_target": "esp32s2.cfg" + }, + "frameworks": [ + "arduino", + "espidf" + ], + "name": "WEMOS LOLIN S2 Mini", + "upload": { + "flash_size": "4MB", + "maximum_ram_size": 327680, + "maximum_size": 4194304, + "use_1200bps_touch": true, + "wait_for_upload_port": true, + "require_upload_port": true, + "speed": 921600 + }, + "url": "https://www.wemos.cc/en/latest/s2/s2_mini.html", + "vendor": "WEMOS" + } \ No newline at end of file diff --git a/boards/s3_16M_P.json b/boards/s3_16M_P.json new file mode 100644 index 000000000..39a795ee5 --- /dev/null +++ b/boards/s3_16M_P.json @@ -0,0 +1,40 @@ +{ + "build": { + "core": "esp32", + "extra_flags": [ + "-DBOARD_HAS_PSRAM", + "-DARDUINO_USB_MODE=1", + "-DARDUINO_USB_CDC_ON_BOOT=1", + "-DARDUINO_RUNNING_CORE=1", + "-DARDUINO_EVENT_RUNNING_CORE=1" + ], + "f_cpu": "240000000L", + "f_flash": "80000000L", + "flash_mode": "qio", + "mcu": "esp32s3", + "variant": "esp32s3" + }, + "connectivity": [ + "wifi" + ], + "debug": { + "openocd_target": "esp32s3.cfg" + }, + "frameworks": [ + "arduino", + "espidf" + ], + "name": "Espressif Generic ESP32-S3 16M Flash OPI PSRAM, 4608KB Code/OTA, 2MB FS", + "upload": { + "flash_size": "16MB", + "maximum_ram_size": 327680, + "maximum_size": 16777216, + "require_upload_port": true, + "speed": 460800 + }, + "download": { + "speed": 230400 + }, + "url": "https://docs.espressif.com/projects/esp-idf/en/latest/esp32s3/hw-reference/esp32s3/", + "vendor": "Espressif" + } \ No newline at end of file diff --git a/boards/s3_32M_P.json b/boards/s3_32M_P.json new file mode 100644 index 000000000..c247db39e --- /dev/null +++ b/boards/s3_32M_P.json @@ -0,0 +1,37 @@ +{ + "build": { + "arduino":{ + "memory_type": "opi_opi" + }, + "core": "esp32", + "extra_flags": "-DBOARD_HAS_PSRAM", + "f_cpu": "240000000L", + "f_flash": "80000000L", + "flash_mode": "opi", + "mcu": "esp32s3", + "variant": "esp32s3" + }, + "connectivity": [ + "wifi" + ], + "debug": { + "openocd_target": "esp32s3.cfg" + }, + "frameworks": [ + "arduino", + "espidf" + ], + "name": "Espressif Generic ESP32-S3 32M Flash OPI PSRAM, 4608KB Code/OTA, 2MB FS", + "upload": { + "flash_size": "32MB", + "maximum_ram_size": 327680, + "maximum_size": 16777216, + "require_upload_port": true, + "speed": 460800 + }, + "download": { + "speed": 230400 + }, + "url": "https://docs.espressif.com/projects/esp-idf/en/latest/esp32s3/hw-reference/esp32s3/", + "vendor": "Espressif" + } \ No newline at end of file diff --git a/boards/s_16M.json b/boards/s_16M.json new file mode 100644 index 000000000..6362f296d --- /dev/null +++ b/boards/s_16M.json @@ -0,0 +1,35 @@ +{ + "build": { + "core": "esp32", + "extra_flags": "-DTASMOTA_SDK", + "f_cpu": "240000000L", + "f_flash": "80000000L", + "flash_mode": "dio", + "mcu": "esp32", + "variant": "esp32" + }, + "connectivity": [ + "wifi", + "ethernet" + ], + "debug": { + "openocd_target": "esp32.cfg" + }, + "frameworks": [ + "arduino", + "espidf" + ], + "name": "Espressif Generic ESP32 16M Flash, Tasmota 4608KB Code/OTA, 2MB FS", + "upload": { + "flash_size": "16MB", + "maximum_ram_size": 327680, + "maximum_size": 16777216, + "require_upload_port": true, + "speed": 460800 + }, + "download": { + "speed": 230400 + }, + "url": "https://en.wikipedia.org/wiki/ESP32", + "vendor": "Espressif" + } \ No newline at end of file diff --git a/boards/s_16M_P.json b/boards/s_16M_P.json new file mode 100644 index 000000000..3ed91d6e5 --- /dev/null +++ b/boards/s_16M_P.json @@ -0,0 +1,35 @@ +{ + "build": { + "core": "esp32", + "extra_flags": "-DBOARD_HAS_PSRAM", + "f_cpu": "240000000L", + "f_flash": "80000000L", + "flash_mode": "dio", + "mcu": "esp32", + "variant": "esp32" + }, + "connectivity": [ + "wifi", + "ethernet" + ], + "debug": { + "openocd_target": "esp32.cfg" + }, + "frameworks": [ + "arduino", + "espidf" + ], + "name": "Espressif Generic ESP32 16M Flash DIO PSRAM, 4608KB Code/OTA, 2MB FS", + "upload": { + "flash_size": "16MB", + "maximum_ram_size": 327680, + "maximum_size": 16777216, + "require_upload_port": true, + "speed": 460800 + }, + "download": { + "speed": 230400 + }, + "url": "https://en.wikipedia.org/wiki/ESP32", + "vendor": "Espressif" + } \ No newline at end of file diff --git a/boards/s_4M.json b/boards/s_4M.json new file mode 100644 index 000000000..13a127242 --- /dev/null +++ b/boards/s_4M.json @@ -0,0 +1,34 @@ +{ + "build": { + "core": "esp32", + "extra_flags": "-DTASMOTA_SDK", + "f_cpu": "240000000L", + "f_flash": "40000000L", + "flash_mode": "dio", + "mcu": "esp32", + "variant": "esp32" + }, + "connectivity": [ + "wifi" + ], + "debug": { + "openocd_target": "esp32.cfg" + }, + "frameworks": [ + "arduino", + "espidf" + ], + "name": "Espressif Generic ESP32 4M Flash, Tasmota 4608KB Code/OTA, 2MB FS", + "upload": { + "flash_size": "4MB", + "maximum_ram_size": 327680, + "maximum_size": 4194304, + "require_upload_port": true, + "speed": 460800 + }, + "download": { + "speed": 230400 + }, + "url": "https://en.wikipedia.org/wiki/ESP32", + "vendor": "Espressif" + } \ No newline at end of file diff --git a/interface/package.json b/interface/package.json index ba892c2b9..8ec373105 100644 --- a/interface/package.json +++ b/interface/package.json @@ -24,8 +24,8 @@ "@alova/adapter-xhr": "2.1.0", "@emotion/react": "^11.14.0", "@emotion/styled": "^11.14.0", - "@mui/icons-material": "^6.3.0", - "@mui/material": "^6.3.0", + "@mui/icons-material": "^6.2.1", + "@mui/material": "^6.2.1", "@table-library/react-table-library": "4.1.7", "alova": "3.2.7", "async-validator": "^4.2.5", @@ -44,10 +44,10 @@ "@babel/core": "^7.26.0", "@eslint/js": "^9.17.0", "@preact/compat": "^18.3.1", - "@preact/preset-vite": "^2.9.3", - "@trivago/prettier-plugin-sort-imports": "^5.2.0", + "@preact/preset-vite": "^2.9.4", + "@trivago/prettier-plugin-sort-imports": "^5.2.1", "@types/formidable": "^3", - "@types/node": "^22.10.2", + "@types/node": "^22.10.5", "@types/react": "^19.0.2", "@types/react-dom": "^19.0.2", "concurrently": "^9.1.2", @@ -58,7 +58,7 @@ "rollup-plugin-visualizer": "^5.13.1", "terser": "^5.37.0", "typescript-eslint": "8.19.0", - "vite": "^6.0.6", + "vite": "^6.0.7", "vite-plugin-imagemin": "^0.6.1", "vite-tsconfig-paths": "^5.1.4" }, diff --git a/interface/progmem-generator.js b/interface/progmem-generator.js index aca4bddf8..52ec26625 100644 --- a/interface/progmem-generator.js +++ b/interface/progmem-generator.js @@ -12,7 +12,7 @@ import zlib from 'zlib'; const ARDUINO_INCLUDES = '#include \n\n'; const INDENT = ' '; -const outputPath = '../lib/framework/WWWData.h'; +const outputPath = '../src/ESP32React/WWWData.h'; const sourcePath = './dist'; const bytesPerLine = 20; var totalSize = 0; diff --git a/interface/src/app/main/Dashboard.tsx b/interface/src/app/main/Dashboard.tsx index a8e2b957e..426af7a92 100644 --- a/interface/src/app/main/Dashboard.tsx +++ b/interface/src/app/main/Dashboard.tsx @@ -3,6 +3,7 @@ import { IconContext } from 'react-icons/lib'; import { toast } from 'react-toastify'; import ChevronRightIcon from '@mui/icons-material/ChevronRight'; +import ConstructionIcon from '@mui/icons-material/Construction'; import EditIcon from '@mui/icons-material/Edit'; import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; import UnfoldLessIcon from '@mui/icons-material/UnfoldLess'; @@ -235,7 +236,10 @@ const Dashboard = () => { - {LL.DASHBOARD_1()}. + {LL.DASHBOARD_1()}  + diff --git a/interface/src/components/layout/LayoutMenu.tsx b/interface/src/components/layout/LayoutMenu.tsx index 713770f12..c9f7aca68 100644 --- a/interface/src/components/layout/LayoutMenu.tsx +++ b/interface/src/components/layout/LayoutMenu.tsx @@ -73,11 +73,6 @@ const LayoutMenu = () => { > { // color: menuOpen ? 'rgba(0,0,0,0)' : 'rgba(255,255,255,0.5)' // }} sx={{ my: 0 }} + slotProps={{ + primary: { + fontWeight: '600', + mb: '2px', + color: 'lightblue' + } + }} /> { )} - { - - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - - This version of the GNU Lesser General Public License incorporates -the terms and conditions of version 3 of the GNU General Public -License, supplemented by the additional permissions listed below. - - 0. Additional Definitions. - - As used herein, "this License" refers to version 3 of the GNU Lesser -General Public License, and the "GNU GPL" refers to version 3 of the GNU -General Public License. - - "The Library" refers to a covered work governed by this License, -other than an Application or a Combined Work as defined below. - - An "Application" is any work that makes use of an interface provided -by the Library, but which is not otherwise based on the Library. -Defining a subclass of a class defined by the Library is deemed a mode -of using an interface provided by the Library. - - A "Combined Work" is a work produced by combining or linking an -Application with the Library. The particular version of the Library -with which the Combined Work was made is also called the "Linked -Version". - - The "Minimal Corresponding Source" for a Combined Work means the -Corresponding Source for the Combined Work, excluding any source code -for portions of the Combined Work that, considered in isolation, are -based on the Application, and not on the Linked Version. - - The "Corresponding Application Code" for a Combined Work means the -object code and/or source code for the Application, including any data -and utility programs needed for reproducing the Combined Work from the -Application, but excluding the System Libraries of the Combined Work. - - 1. Exception to Section 3 of the GNU GPL. - - You may convey a covered work under sections 3 and 4 of this License -without being bound by section 3 of the GNU GPL. - - 2. Conveying Modified Versions. - - If you modify a copy of the Library, and, in your modifications, a -facility refers to a function or data to be supplied by an Application -that uses the facility (other than as an argument passed when the -facility is invoked), then you may convey a copy of the modified -version: - - a) under this License, provided that you make a good faith effort to - ensure that, in the event an Application does not supply the - function or data, the facility still operates, and performs - whatever part of its purpose remains meaningful, or - - b) under the GNU GPL, with none of the additional permissions of - this License applicable to that copy. - - 3. Object Code Incorporating Material from Library Header Files. - - The object code form of an Application may incorporate material from -a header file that is part of the Library. You may convey such object -code under terms of your choice, provided that, if the incorporated -material is not limited to numerical parameters, data structure -layouts and accessors, or small macros, inline functions and templates -(ten or fewer lines in length), you do both of the following: - - a) Give prominent notice with each copy of the object code that the - Library is used in it and that the Library and its use are - covered by this License. - - b) Accompany the object code with a copy of the GNU GPL and this license - document. - - 4. Combined Works. - - You may convey a Combined Work under terms of your choice that, -taken together, effectively do not restrict modification of the -portions of the Library contained in the Combined Work and reverse -engineering for debugging such modifications, if you also do each of -the following: - - a) Give prominent notice with each copy of the Combined Work that - the Library is used in it and that the Library and its use are - covered by this License. - - b) Accompany the Combined Work with a copy of the GNU GPL and this license - document. - - c) For a Combined Work that displays copyright notices during - execution, include the copyright notice for the Library among - these notices, as well as a reference directing the user to the - copies of the GNU GPL and this license document. - - d) Do one of the following: - - 0) Convey the Minimal Corresponding Source under the terms of this - License, and the Corresponding Application Code in a form - suitable for, and under terms that permit, the user to - recombine or relink the Application with a modified version of - the Linked Version to produce a modified Combined Work, in the - manner specified by section 6 of the GNU GPL for conveying - Corresponding Source. - - 1) Use a suitable shared library mechanism for linking with the - Library. A suitable mechanism is one that (a) uses at run time - a copy of the Library already present on the user's computer - system, and (b) will operate properly with a modified version - of the Library that is interface-compatible with the Linked - Version. - - e) Provide Installation Information, but only if you would otherwise - be required to provide such information under section 6 of the - GNU GPL, and only to the extent that such information is - necessary to install and execute a modified version of the - Combined Work produced by recombining or relinking the - Application with a modified version of the Linked Version. (If - you use option 4d0, the Installation Information must accompany - the Minimal Corresponding Source and Corresponding Application - Code. If you use option 4d1, you must provide the Installation - Information in the manner specified by section 6 of the GNU GPL - for conveying Corresponding Source.) - - 5. Combined Libraries. - - You may place library facilities that are a work based on the -Library side by side in a single library together with other library -facilities that are not Applications and are not covered by this -License, and convey such a combined library under terms of your -choice, if you do both of the following: - - a) Accompany the combined library with a copy of the same work based - on the Library, uncombined with any other library facilities, - conveyed under the terms of this License. - - b) Give prominent notice with the combined library that part of it - is a work based on the Library, and explaining where to find the - accompanying uncombined form of the same work. - - 6. Revised Versions of the GNU Lesser General Public License. - - The Free Software Foundation may publish revised and/or new versions -of the GNU Lesser General Public License from time to time. Such new -versions will be similar in spirit to the present version, but may -differ in detail to address new problems or concerns. - - Each version is given a distinguishing version number. If the -Library as you received it specifies that a certain numbered version -of the GNU Lesser General Public License "or any later version" -applies to it, you have the option of following the terms and -conditions either of that published version or of any later version -published by the Free Software Foundation. If the Library as you -received it does not specify a version number of the GNU Lesser -General Public License, you may choose any version of the GNU Lesser -General Public License ever published by the Free Software Foundation. - - If the Library as you received it specifies that a proxy can decide -whether future versions of the GNU Lesser General Public License shall -apply, that proxy's public statement of acceptance of any version is -permanent authorization for you to choose that version for the -Library. diff --git a/lib/AsyncTCP/README.md b/lib/AsyncTCP/README.md deleted file mode 100644 index 5eb47541b..000000000 --- a/lib/AsyncTCP/README.md +++ /dev/null @@ -1,14 +0,0 @@ -# AsyncTCP - -![Build Status](https://github.com/esphome/AsyncTCP/actions/workflows/push.yml/badge.svg) -A fork of the [AsyncTCP](https://github.com/me-no-dev/AsyncTCP) library by [@me-no-dev](https://github.com/me-no-dev) for [ESPHome](https://esphome.io). - -### Async TCP Library for ESP32 Arduino - -This is a fully asynchronous TCP library, aimed at enabling trouble-free, multi-connection network environment for Espressif's ESP32 MCUs. - -This library is the base for [ESPAsyncWebServer](https://github.com/me-no-dev/ESPAsyncWebServer) - -## AsyncClient and AsyncServer - -The base classes on which everything else is built. They expose all possible scenarios, but are really raw and require more skills to use. diff --git a/lib/AsyncTCP/src/AsyncTCP.cpp b/lib/AsyncTCP/src/AsyncTCP.cpp deleted file mode 100644 index c3b0e5e32..000000000 --- a/lib/AsyncTCP/src/AsyncTCP.cpp +++ /dev/null @@ -1,1631 +0,0 @@ -/* - Asynchronous TCP library for Espressif MCUs - - Copyright (c) 2016 Hristo Gochkov. All rights reserved. - This file is part of the esp8266 core 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 - */ - -#include "Arduino.h" - -#include "AsyncTCP.h" -extern "C" { -#include "lwip/opt.h" -#include "lwip/tcp.h" -#include "lwip/inet.h" -#include "lwip/dns.h" -#include "lwip/err.h" -} -#if CONFIG_ASYNC_TCP_USE_WDT -#include "esp_task_wdt.h" -#endif - -// Required for: -// https://github.com/espressif/arduino-esp32/blob/3.0.3/libraries/Network/src/NetworkInterface.cpp#L37-L47 -#if ESP_IDF_VERSION_MAJOR >= 5 -// #include -#endif - -#define TAG "AsyncTCP" - -// https://github.com/espressif/arduino-esp32/issues/10526 -#ifdef CONFIG_LWIP_TCPIP_CORE_LOCKING -#define TCP_MUTEX_LOCK() \ - if (!sys_thread_tcpip(LWIP_CORE_LOCK_QUERY_HOLDER)) { \ - LOCK_TCPIP_CORE(); \ - } - -#define TCP_MUTEX_UNLOCK() \ - if (sys_thread_tcpip(LWIP_CORE_LOCK_QUERY_HOLDER)) { \ - UNLOCK_TCPIP_CORE(); \ - } -#else // CONFIG_LWIP_TCPIP_CORE_LOCKING -#define TCP_MUTEX_LOCK() -#define TCP_MUTEX_UNLOCK() -#endif // CONFIG_LWIP_TCPIP_CORE_LOCKING - -#define INVALID_CLOSED_SLOT -1 - -/* - * TCP/IP Event Task - * */ - -typedef enum { - LWIP_TCP_SENT, - LWIP_TCP_RECV, - LWIP_TCP_FIN, - LWIP_TCP_ERROR, - LWIP_TCP_POLL, - LWIP_TCP_CLEAR, - LWIP_TCP_ACCEPT, - LWIP_TCP_CONNECTED, - LWIP_TCP_DNS -} lwip_event_t; - -typedef struct { - lwip_event_t event; - void * arg; - union { - struct { - tcp_pcb * pcb; - int8_t err; - } connected; - struct { - int8_t err; - } error; - struct { - tcp_pcb * pcb; - uint16_t len; - } sent; - struct { - tcp_pcb * pcb; - pbuf * pb; - int8_t err; - } recv; - struct { - tcp_pcb * pcb; - int8_t err; - } fin; - struct { - tcp_pcb * pcb; - } poll; - struct { - AsyncClient * client; - } accept; - struct { - const char * name; - ip_addr_t addr; - } dns; - }; -} lwip_event_packet_t; - -static QueueHandle_t _async_queue; -static TaskHandle_t _async_service_task_handle = NULL; - - -SemaphoreHandle_t _slots_lock; -const int _number_of_closed_slots = CONFIG_LWIP_MAX_ACTIVE_TCP; -static uint32_t _closed_slots[_number_of_closed_slots]; -static uint32_t _closed_index = []() { - _slots_lock = xSemaphoreCreateBinary(); - xSemaphoreGive(_slots_lock); - for (int i = 0; i < _number_of_closed_slots; ++i) { - _closed_slots[i] = 1; - } - return 1; -}(); - - -static inline bool _init_async_event_queue() { - if (!_async_queue) { - _async_queue = xQueueCreate(CONFIG_ASYNC_TCP_QUEUE_SIZE, sizeof(lwip_event_packet_t *)); - if (!_async_queue) { - return false; - } - } - return true; -} - -static inline bool _send_async_event(lwip_event_packet_t ** e) { - return _async_queue && xQueueSend(_async_queue, e, portMAX_DELAY) == pdPASS; -} - -static inline bool _prepend_async_event(lwip_event_packet_t ** e) { - return _async_queue && xQueueSendToFront(_async_queue, e, portMAX_DELAY) == pdPASS; -} - -static inline bool _get_async_event(lwip_event_packet_t ** e) { - return _async_queue && xQueueReceive(_async_queue, e, portMAX_DELAY) == pdPASS; -} - -static bool _remove_events_with_arg(void * arg) { - lwip_event_packet_t * first_packet = NULL; - lwip_event_packet_t * packet = NULL; - - if (!_async_queue) { - return false; - } - //figure out which is the first packet so we can keep the order - while (!first_packet) { - if (xQueueReceive(_async_queue, &first_packet, 0) != pdPASS) { - return false; - } - //discard packet if matching - if ((int)first_packet->arg == (int)arg) { - free(first_packet); - first_packet = NULL; - //return first packet to the back of the queue - } else if (xQueueSend(_async_queue, &first_packet, portMAX_DELAY) != pdPASS) { - return false; - } - } - - while (xQueuePeek(_async_queue, &packet, 0) == pdPASS && packet != first_packet) { - if (xQueueReceive(_async_queue, &packet, 0) != pdPASS) { - return false; - } - if ((int)packet->arg == (int)arg) { - free(packet); - packet = NULL; - } else if (xQueueSend(_async_queue, &packet, portMAX_DELAY) != pdPASS) { - return false; - } - } - return true; -} - -static void _handle_async_event(lwip_event_packet_t * e) { - if (e->arg == NULL) { - // do nothing when arg is NULL - //ets_printf("event arg == NULL: 0x%08x\n", e->recv.pcb); - } else if (e->event == LWIP_TCP_CLEAR) { - _remove_events_with_arg(e->arg); - } else if (e->event == LWIP_TCP_RECV) { - //ets_printf("-R: 0x%08x\n", e->recv.pcb); - AsyncClient::_s_recv(e->arg, e->recv.pcb, e->recv.pb, e->recv.err); - } else if (e->event == LWIP_TCP_FIN) { - //ets_printf("-F: 0x%08x\n", e->fin.pcb); - AsyncClient::_s_fin(e->arg, e->fin.pcb, e->fin.err); - } else if (e->event == LWIP_TCP_SENT) { - //ets_printf("-S: 0x%08x\n", e->sent.pcb); - AsyncClient::_s_sent(e->arg, e->sent.pcb, e->sent.len); - } else if (e->event == LWIP_TCP_POLL) { - //ets_printf("-P: 0x%08x\n", e->poll.pcb); - AsyncClient::_s_poll(e->arg, e->poll.pcb); - } else if (e->event == LWIP_TCP_ERROR) { - //ets_printf("-E: 0x%08x %d\n", e->arg, e->error.err); - AsyncClient::_s_error(e->arg, e->error.err); - } else if (e->event == LWIP_TCP_CONNECTED) { - //ets_printf("C: 0x%08x 0x%08x %d\n", e->arg, e->connected.pcb, e->connected.err); - AsyncClient::_s_connected(e->arg, e->connected.pcb, e->connected.err); - } else if (e->event == LWIP_TCP_ACCEPT) { - //ets_printf("A: 0x%08x 0x%08x\n", e->arg, e->accept.client); - AsyncServer::_s_accepted(e->arg, e->accept.client); - } else if (e->event == LWIP_TCP_DNS) { - //ets_printf("D: 0x%08x %s = %s\n", e->arg, e->dns.name, ipaddr_ntoa(&e->dns.addr)); - AsyncClient::_s_dns_found(e->dns.name, &e->dns.addr, e->arg); - } - free((void *)(e)); -} - -static void _async_service_task(void * pvParameters) { - lwip_event_packet_t * packet = NULL; - for (;;) { - if (_get_async_event(&packet)) { -#if CONFIG_ASYNC_TCP_USE_WDT - if (esp_task_wdt_add(NULL) != ESP_OK) { - log_e("Failed to add async task to WDT"); - } -#endif - _handle_async_event(packet); -#if CONFIG_ASYNC_TCP_USE_WDT - if (esp_task_wdt_delete(NULL) != ESP_OK) { - log_e("Failed to remove loop task from WDT"); - } -#endif - } - } - vTaskDelete(NULL); - _async_service_task_handle = NULL; -} -/* -static void _stop_async_task(){ - if(_async_service_task_handle){ - vTaskDelete(_async_service_task_handle); - _async_service_task_handle = NULL; - } -} -*/ - -static bool customTaskCreateUniversal(TaskFunction_t pxTaskCode, - const char * const pcName, - const uint32_t usStackDepth, - void * const pvParameters, - UBaseType_t uxPriority, - TaskHandle_t * const pxCreatedTask, - const BaseType_t xCoreID) { -#ifndef CONFIG_FREERTOS_UNICORE - if (xCoreID >= 0 && xCoreID < 2) { - return xTaskCreatePinnedToCore(pxTaskCode, pcName, usStackDepth, pvParameters, uxPriority, pxCreatedTask, xCoreID); - } else { -#endif - return xTaskCreate(pxTaskCode, pcName, usStackDepth, pvParameters, uxPriority, pxCreatedTask); -#ifndef CONFIG_FREERTOS_UNICORE - } -#endif -} - -static bool _start_async_task() { - if (!_init_async_event_queue()) { - return false; - } - if (!_async_service_task_handle) { - customTaskCreateUniversal(_async_service_task, - "async_tcp", - CONFIG_ASYNC_TCP_STACK_SIZE, - NULL, - CONFIG_ASYNC_TCP_PRIORITY, - &_async_service_task_handle, - CONFIG_ASYNC_TCP_RUNNING_CORE); - if (!_async_service_task_handle) { - return false; - } - } - return true; -} - -/* - * LwIP Callbacks - * */ - -static int8_t _tcp_clear_events(void * arg) { - lwip_event_packet_t * e = (lwip_event_packet_t *)malloc(sizeof(lwip_event_packet_t)); - e->event = LWIP_TCP_CLEAR; - e->arg = arg; - if (!_prepend_async_event(&e)) { - free((void *)(e)); - } - return ERR_OK; -} - -static int8_t _tcp_connected(void * arg, tcp_pcb * pcb, int8_t err) { - //ets_printf("+C: 0x%08x\n", pcb); - lwip_event_packet_t * e = (lwip_event_packet_t *)malloc(sizeof(lwip_event_packet_t)); - e->event = LWIP_TCP_CONNECTED; - e->arg = arg; - e->connected.pcb = pcb; - e->connected.err = err; - if (!_prepend_async_event(&e)) { - free((void *)(e)); - } - return ERR_OK; -} - -static int8_t _tcp_poll(void * arg, struct tcp_pcb * pcb) { - //ets_printf("+P: 0x%08x\n", pcb); - lwip_event_packet_t * e = (lwip_event_packet_t *)malloc(sizeof(lwip_event_packet_t)); - e->event = LWIP_TCP_POLL; - e->arg = arg; - e->poll.pcb = pcb; - if (!_send_async_event(&e)) { - free((void *)(e)); - } - return ERR_OK; -} - -static int8_t _tcp_recv(void * arg, struct tcp_pcb * pcb, struct pbuf * pb, int8_t err) { - lwip_event_packet_t * e = (lwip_event_packet_t *)malloc(sizeof(lwip_event_packet_t)); - e->arg = arg; - if (pb) { - //ets_printf("+R: 0x%08x\n", pcb); - e->event = LWIP_TCP_RECV; - e->recv.pcb = pcb; - e->recv.pb = pb; - e->recv.err = err; - } else { - //ets_printf("+F: 0x%08x\n", pcb); - e->event = LWIP_TCP_FIN; - e->fin.pcb = pcb; - e->fin.err = err; - //close the PCB in LwIP thread - AsyncClient::_s_lwip_fin(e->arg, e->fin.pcb, e->fin.err); - } - if (!_send_async_event(&e)) { - free((void *)(e)); - } - return ERR_OK; -} - -static int8_t _tcp_sent(void * arg, struct tcp_pcb * pcb, uint16_t len) { - //ets_printf("+S: 0x%08x\n", pcb); - lwip_event_packet_t * e = (lwip_event_packet_t *)malloc(sizeof(lwip_event_packet_t)); - e->event = LWIP_TCP_SENT; - e->arg = arg; - e->sent.pcb = pcb; - e->sent.len = len; - if (!_send_async_event(&e)) { - free((void *)(e)); - } - return ERR_OK; -} - -static void _tcp_error(void * arg, int8_t err) { - //ets_printf("+E: 0x%08x\n", arg); - lwip_event_packet_t * e = (lwip_event_packet_t *)malloc(sizeof(lwip_event_packet_t)); - e->event = LWIP_TCP_ERROR; - e->arg = arg; - e->error.err = err; - if (!_send_async_event(&e)) { - free((void *)(e)); - } -} - -static void _tcp_dns_found(const char * name, struct ip_addr * ipaddr, void * arg) { - lwip_event_packet_t * e = (lwip_event_packet_t *)malloc(sizeof(lwip_event_packet_t)); - //ets_printf("+DNS: name=%s ipaddr=0x%08x arg=%x\n", name, ipaddr, arg); - e->event = LWIP_TCP_DNS; - e->arg = arg; - e->dns.name = name; - if (ipaddr) { - memcpy(&e->dns.addr, ipaddr, sizeof(struct ip_addr)); - } else { - memset(&e->dns.addr, 0, sizeof(e->dns.addr)); - } - if (!_send_async_event(&e)) { - free((void *)(e)); - } -} - -//Used to switch out from LwIP thread -static int8_t _tcp_accept(void * arg, AsyncClient * client) { - lwip_event_packet_t * e = (lwip_event_packet_t *)malloc(sizeof(lwip_event_packet_t)); - e->event = LWIP_TCP_ACCEPT; - e->arg = arg; - e->accept.client = client; - if (!_prepend_async_event(&e)) { - free((void *)(e)); - } - return ERR_OK; -} - -/* - * TCP/IP API Calls - * */ - -#include "lwip/priv/tcpip_priv.h" - -typedef struct { - struct tcpip_api_call_data call; - tcp_pcb * pcb; - int8_t closed_slot; - int8_t err; - union { - struct { - const char * data; - size_t size; - uint8_t apiflags; - } write; - size_t received; - struct { - ip_addr_t * addr; - uint16_t port; - tcp_connected_fn cb; - } connect; - struct { - ip_addr_t * addr; - uint16_t port; - } bind; - uint8_t backlog; - }; -} tcp_api_call_t; - -static err_t _tcp_output_api(struct tcpip_api_call_data * api_call_msg) { - tcp_api_call_t * msg = (tcp_api_call_t *)api_call_msg; - msg->err = ERR_CONN; - if (msg->closed_slot == INVALID_CLOSED_SLOT || !_closed_slots[msg->closed_slot]) { - msg->err = tcp_output(msg->pcb); - } - return msg->err; -} - -static esp_err_t _tcp_output(tcp_pcb * pcb, int8_t closed_slot) { - if (!pcb) { - return ERR_CONN; - } - tcp_api_call_t msg; - msg.pcb = pcb; - msg.closed_slot = closed_slot; - tcpip_api_call(_tcp_output_api, (struct tcpip_api_call_data *)&msg); - return msg.err; -} - -static err_t _tcp_write_api(struct tcpip_api_call_data * api_call_msg) { - tcp_api_call_t * msg = (tcp_api_call_t *)api_call_msg; - msg->err = ERR_CONN; - if (msg->closed_slot == INVALID_CLOSED_SLOT || !_closed_slots[msg->closed_slot]) { - msg->err = tcp_write(msg->pcb, msg->write.data, msg->write.size, msg->write.apiflags); - } - return msg->err; -} - -static esp_err_t _tcp_write(tcp_pcb * pcb, int8_t closed_slot, const char * data, size_t size, uint8_t apiflags) { - if (!pcb) { - return ERR_CONN; - } - tcp_api_call_t msg; - msg.pcb = pcb; - msg.closed_slot = closed_slot; - msg.write.data = data; - msg.write.size = size; - msg.write.apiflags = apiflags; - tcpip_api_call(_tcp_write_api, (struct tcpip_api_call_data *)&msg); - return msg.err; -} - -static err_t _tcp_recved_api(struct tcpip_api_call_data * api_call_msg) { - tcp_api_call_t * msg = (tcp_api_call_t *)api_call_msg; - msg->err = ERR_CONN; - if (msg->closed_slot == INVALID_CLOSED_SLOT || !_closed_slots[msg->closed_slot]) { - // if(msg->closed_slot != INVALID_CLOSED_SLOT && !_closed_slots[msg->closed_slot]) { - // if(msg->closed_slot != INVALID_CLOSED_SLOT) { - msg->err = 0; - tcp_recved(msg->pcb, msg->received); - } - return msg->err; -} - -static esp_err_t _tcp_recved(tcp_pcb * pcb, int8_t closed_slot, size_t len) { - if (!pcb) { - return ERR_CONN; - } - tcp_api_call_t msg; - msg.pcb = pcb; - msg.closed_slot = closed_slot; - msg.received = len; - tcpip_api_call(_tcp_recved_api, (struct tcpip_api_call_data *)&msg); - return msg.err; -} - -static err_t _tcp_close_api(struct tcpip_api_call_data * api_call_msg) { - tcp_api_call_t * msg = (tcp_api_call_t *)api_call_msg; - msg->err = ERR_CONN; - if (msg->closed_slot == INVALID_CLOSED_SLOT || !_closed_slots[msg->closed_slot]) { - msg->err = tcp_close(msg->pcb); - } - return msg->err; -} - -static esp_err_t _tcp_close(tcp_pcb * pcb, int8_t closed_slot) { - if (!pcb) { - return ERR_CONN; - } - tcp_api_call_t msg; - msg.pcb = pcb; - msg.closed_slot = closed_slot; - tcpip_api_call(_tcp_close_api, (struct tcpip_api_call_data *)&msg); - return msg.err; -} - -static err_t _tcp_abort_api(struct tcpip_api_call_data * api_call_msg) { - tcp_api_call_t * msg = (tcp_api_call_t *)api_call_msg; - msg->err = ERR_CONN; - if (msg->closed_slot == INVALID_CLOSED_SLOT || !_closed_slots[msg->closed_slot]) { - tcp_abort(msg->pcb); - } - return msg->err; -} - -static esp_err_t _tcp_abort(tcp_pcb * pcb, int8_t closed_slot) { - if (!pcb) { - return ERR_CONN; - } - tcp_api_call_t msg; - msg.pcb = pcb; - msg.closed_slot = closed_slot; - tcpip_api_call(_tcp_abort_api, (struct tcpip_api_call_data *)&msg); - return msg.err; -} - -static err_t _tcp_connect_api(struct tcpip_api_call_data * api_call_msg) { - tcp_api_call_t * msg = (tcp_api_call_t *)api_call_msg; - msg->err = tcp_connect(msg->pcb, msg->connect.addr, msg->connect.port, msg->connect.cb); - return msg->err; -} - -static esp_err_t _tcp_connect(tcp_pcb * pcb, int8_t closed_slot, ip_addr_t * addr, uint16_t port, tcp_connected_fn cb) { - if (!pcb) { - return ESP_FAIL; - } - tcp_api_call_t msg; - msg.pcb = pcb; - msg.closed_slot = closed_slot; - msg.connect.addr = addr; - msg.connect.port = port; - msg.connect.cb = cb; - tcpip_api_call(_tcp_connect_api, (struct tcpip_api_call_data *)&msg); - return msg.err; -} - -static err_t _tcp_bind_api(struct tcpip_api_call_data * api_call_msg) { - tcp_api_call_t * msg = (tcp_api_call_t *)api_call_msg; - msg->err = tcp_bind(msg->pcb, msg->bind.addr, msg->bind.port); - return msg->err; -} - -static esp_err_t _tcp_bind(tcp_pcb * pcb, ip_addr_t * addr, uint16_t port) { - if (!pcb) { - return ESP_FAIL; - } - tcp_api_call_t msg; - msg.pcb = pcb; - msg.closed_slot = -1; - msg.bind.addr = addr; - msg.bind.port = port; - tcpip_api_call(_tcp_bind_api, (struct tcpip_api_call_data *)&msg); - return msg.err; -} - -static err_t _tcp_listen_api(struct tcpip_api_call_data * api_call_msg) { - tcp_api_call_t * msg = (tcp_api_call_t *)api_call_msg; - msg->err = 0; - msg->pcb = tcp_listen_with_backlog(msg->pcb, msg->backlog); - return msg->err; -} - -static tcp_pcb * _tcp_listen_with_backlog(tcp_pcb * pcb, uint8_t backlog) { - if (!pcb) { - return NULL; - } - tcp_api_call_t msg; - msg.pcb = pcb; - msg.closed_slot = -1; - msg.backlog = backlog ? backlog : 0xFF; - tcpip_api_call(_tcp_listen_api, (struct tcpip_api_call_data *)&msg); - return msg.pcb; -} - - - -/* - Async TCP Client - */ - -AsyncClient::AsyncClient(tcp_pcb * pcb) - : _connect_cb(0) - , _connect_cb_arg(0) - , _discard_cb(0) - , _discard_cb_arg(0) - , _sent_cb(0) - , _sent_cb_arg(0) - , _error_cb(0) - , _error_cb_arg(0) - , _recv_cb(0) - , _recv_cb_arg(0) - , _pb_cb(0) - , _pb_cb_arg(0) - , _timeout_cb(0) - , _timeout_cb_arg(0) - , _ack_pcb(true) - , _tx_last_packet(0) - , _rx_timeout(0) - , _rx_last_ack(0) - , _ack_timeout(CONFIG_ASYNC_TCP_MAX_ACK_TIME) - , _connect_port(0) - , prev(NULL) - , next(NULL) { - _pcb = pcb; - _closed_slot = INVALID_CLOSED_SLOT; - if (_pcb) { - _rx_last_packet = millis(); - tcp_arg(_pcb, this); - tcp_recv(_pcb, &_tcp_recv); - tcp_sent(_pcb, &_tcp_sent); - tcp_err(_pcb, &_tcp_error); - tcp_poll(_pcb, &_tcp_poll, 1); - if (!_allocate_closed_slot()) { - _close(); - } - } -} - -AsyncClient::~AsyncClient() { - if (_pcb) { - _close(); - } - _free_closed_slot(); -} - -/* - * Operators - * */ - -AsyncClient & AsyncClient::operator=(const AsyncClient & other) { - if (_pcb) { - _close(); - } - - _pcb = other._pcb; - _closed_slot = other._closed_slot; - if (_pcb) { - _rx_last_packet = millis(); - tcp_arg(_pcb, this); - tcp_recv(_pcb, &_tcp_recv); - tcp_sent(_pcb, &_tcp_sent); - tcp_err(_pcb, &_tcp_error); - tcp_poll(_pcb, &_tcp_poll, 1); - } - return *this; -} - -bool AsyncClient::operator==(const AsyncClient & other) { - return _pcb == other._pcb; -} - -AsyncClient & AsyncClient::operator+=(const AsyncClient & other) { - if (next == NULL) { - next = (AsyncClient *)(&other); - next->prev = this; - } else { - AsyncClient * c = next; - while (c->next != NULL) { - c = c->next; - } - c->next = (AsyncClient *)(&other); - c->next->prev = c; - } - return *this; -} - -/* - * Callback Setters - * */ - -void AsyncClient::onConnect(AcConnectHandler cb, void * arg) { - _connect_cb = cb; - _connect_cb_arg = arg; -} - -void AsyncClient::onDisconnect(AcConnectHandler cb, void * arg) { - _discard_cb = cb; - _discard_cb_arg = arg; -} - -void AsyncClient::onAck(AcAckHandler cb, void * arg) { - _sent_cb = cb; - _sent_cb_arg = arg; -} - -void AsyncClient::onError(AcErrorHandler cb, void * arg) { - _error_cb = cb; - _error_cb_arg = arg; -} - -void AsyncClient::onData(AcDataHandler cb, void * arg) { - _recv_cb = cb; - _recv_cb_arg = arg; -} - -void AsyncClient::onPacket(AcPacketHandler cb, void * arg) { - _pb_cb = cb; - _pb_cb_arg = arg; -} - -void AsyncClient::onTimeout(AcTimeoutHandler cb, void * arg) { - _timeout_cb = cb; - _timeout_cb_arg = arg; -} - -void AsyncClient::onPoll(AcConnectHandler cb, void * arg) { - _poll_cb = cb; - _poll_cb_arg = arg; -} - -/* - * Main Public Methods - * */ - -bool AsyncClient::_connect(ip_addr_t addr, uint16_t port) { - if (_pcb) { - log_d("already connected, state %d", _pcb->state); - return false; - } - if (!_start_async_task()) { - log_e("failed to start task"); - return false; - } - - if (!_allocate_closed_slot()) { - log_e("failed to allocate: closed slot full"); - return false; - } - - TCP_MUTEX_LOCK(); - tcp_pcb * pcb = tcp_new_ip_type(addr.type); - if (!pcb) { - TCP_MUTEX_UNLOCK(); - log_e("pcb == NULL"); - return false; - } - tcp_arg(pcb, this); - tcp_err(pcb, &_tcp_error); - tcp_recv(pcb, &_tcp_recv); - tcp_sent(pcb, &_tcp_sent); - tcp_poll(pcb, &_tcp_poll, 1); - TCP_MUTEX_UNLOCK(); - - esp_err_t err = _tcp_connect(pcb, _closed_slot, &addr, port, (tcp_connected_fn)&_tcp_connected); - return err == ESP_OK; -} - -bool AsyncClient::connect(const IPAddress & ip, uint16_t port) { - ip_addr_t addr; -#if ESP_IDF_VERSION_MAJOR < 5 - addr.u_addr.ip4.addr = ip; - addr.type = IPADDR_TYPE_V4; -#else - ip.to_ip_addr_t(&addr); -#endif - - return _connect(addr, port); -} - -#if LWIP_IPV6 && ESP_IDF_VERSION_MAJOR < 5 -bool AsyncClient::connect(const IPv6Address & ip, uint16_t port) { - auto ipaddr = static_cast(ip); - ip_addr_t addr = IPADDR6_INIT(ipaddr[0], ipaddr[1], ipaddr[2], ipaddr[3]); - - return _connect(addr, port); -} -#endif - -bool AsyncClient::connect(const char * host, uint16_t port) { - ip_addr_t addr; - - if (!_start_async_task()) { - log_e("failed to start task"); - return false; - } - - err_t err = dns_gethostbyname(host, &addr, (dns_found_callback)&_tcp_dns_found, this); - if (err == ERR_OK) { -#if ESP_IDF_VERSION_MAJOR < 5 -#if LWIP_IPV6 - if (addr.type == IPADDR_TYPE_V6) { - return connect(IPv6Address(addr.u_addr.ip6.addr), port); - } - return connect(IPAddress(addr.u_addr.ip4.addr), port); -#else - return connect(IPAddress(addr.addr), port); -#endif -#else - return _connect(addr, port); -#endif - } else if (err == ERR_INPROGRESS) { - _connect_port = port; - return true; - } - log_d("error: %d", err); - return false; -} - -void AsyncClient::close(bool now) { - if (_pcb) { - _tcp_recved(_pcb, _closed_slot, _rx_ack_len); - } - _close(); -} - -int8_t AsyncClient::abort() { - if (_pcb) { - _tcp_abort(_pcb, _closed_slot); - _pcb = NULL; - } - return ERR_ABRT; -} - -size_t AsyncClient::space() { - if ((_pcb != NULL) && (_pcb->state == ESTABLISHED)) { - return tcp_sndbuf(_pcb); - } - return 0; -} - -size_t AsyncClient::add(const char * data, size_t size, uint8_t apiflags) { - if (!_pcb || size == 0 || data == NULL) { - return 0; - } - size_t room = space(); - if (!room) { - return 0; - } - size_t will_send = (room < size) ? room : size; - int8_t err = ERR_OK; - err = _tcp_write(_pcb, _closed_slot, data, will_send, apiflags); - if (err != ERR_OK) { - return 0; - } - return will_send; -} - -bool AsyncClient::send() { - auto backup = _tx_last_packet; - _tx_last_packet = millis(); - if (_tcp_output(_pcb, _closed_slot) == ERR_OK) { - return true; - } - _tx_last_packet = backup; - return false; -} - -size_t AsyncClient::ack(size_t len) { - if (len > _rx_ack_len) - len = _rx_ack_len; - if (len) { - _tcp_recved(_pcb, _closed_slot, len); - } - _rx_ack_len -= len; - return len; -} - -void AsyncClient::ackPacket(struct pbuf * pb) { - if (!pb) { - return; - } - _tcp_recved(_pcb, _closed_slot, pb->len); - pbuf_free(pb); -} - -/* - * Main Private Methods - * */ - -int8_t AsyncClient::_close() { - //ets_printf("X: 0x%08x\n", (uint32_t)this); - int8_t err = ERR_OK; - if (_pcb) { - TCP_MUTEX_LOCK(); - tcp_arg(_pcb, NULL); - tcp_sent(_pcb, NULL); - tcp_recv(_pcb, NULL); - tcp_err(_pcb, NULL); - tcp_poll(_pcb, NULL, 0); - TCP_MUTEX_UNLOCK(); - _tcp_clear_events(this); - err = _tcp_close(_pcb, _closed_slot); - if (err != ERR_OK) { - err = abort(); - } - _free_closed_slot(); - _pcb = NULL; - if (_discard_cb) { - _discard_cb(_discard_cb_arg, this); - } - } - return err; -} - -bool AsyncClient::_allocate_closed_slot() { - if (_closed_slot != INVALID_CLOSED_SLOT) { - return true; - } - xSemaphoreTake(_slots_lock, portMAX_DELAY); - uint32_t closed_slot_min_index = 0; - for (int i = 0; i < _number_of_closed_slots; ++i) { - if ((_closed_slot == INVALID_CLOSED_SLOT || _closed_slots[i] <= closed_slot_min_index) && _closed_slots[i] != 0) { - closed_slot_min_index = _closed_slots[i]; - _closed_slot = i; - } - } - if (_closed_slot != INVALID_CLOSED_SLOT) { - _closed_slots[_closed_slot] = 0; - } - xSemaphoreGive(_slots_lock); - return (_closed_slot != INVALID_CLOSED_SLOT); -} - -void AsyncClient::_free_closed_slot() { - xSemaphoreTake(_slots_lock, portMAX_DELAY); - if (_closed_slot != INVALID_CLOSED_SLOT) { - _closed_slots[_closed_slot] = _closed_index; - _closed_slot = INVALID_CLOSED_SLOT; - ++_closed_index; - } - xSemaphoreGive(_slots_lock); -} - -/* - * Private Callbacks - * */ - -int8_t AsyncClient::_connected(tcp_pcb * pcb, int8_t err) { - _pcb = reinterpret_cast(pcb); - if (_pcb) { - _rx_last_packet = millis(); - } - if (_connect_cb) { - _connect_cb(_connect_cb_arg, this); - } - return ERR_OK; -} - -void AsyncClient::_error(int8_t err) { - if (_pcb) { - TCP_MUTEX_LOCK(); - tcp_arg(_pcb, NULL); - if (_pcb->state == LISTEN) { - tcp_sent(_pcb, NULL); - tcp_recv(_pcb, NULL); - tcp_err(_pcb, NULL); - tcp_poll(_pcb, NULL, 0); - } - TCP_MUTEX_UNLOCK(); - _free_closed_slot(); - _pcb = NULL; - } - if (_error_cb) { - _error_cb(_error_cb_arg, this, err); - } - if (_discard_cb) { - _discard_cb(_discard_cb_arg, this); - } -} - -//In LwIP Thread -int8_t AsyncClient::_lwip_fin(tcp_pcb * pcb, int8_t err) { - if (!_pcb || pcb != _pcb) { - log_d("0x%08x != 0x%08x", (uint32_t)pcb, (uint32_t)_pcb); - return ERR_OK; - } - tcp_arg(_pcb, NULL); - if (_pcb->state == LISTEN) { - tcp_sent(_pcb, NULL); - tcp_recv(_pcb, NULL); - tcp_err(_pcb, NULL); - tcp_poll(_pcb, NULL, 0); - } - if (tcp_close(_pcb) != ERR_OK) { - tcp_abort(_pcb); - } - _free_closed_slot(); - _pcb = NULL; - return ERR_OK; -} - -//In Async Thread -int8_t AsyncClient::_fin(tcp_pcb * pcb, int8_t err) { - _tcp_clear_events(this); - if (_discard_cb) { - _discard_cb(_discard_cb_arg, this); - } - return ERR_OK; -} - -int8_t AsyncClient::_sent(tcp_pcb * pcb, uint16_t len) { - _rx_last_ack = _rx_last_packet = millis(); - if (_sent_cb) { - _sent_cb(_sent_cb_arg, this, len, (_rx_last_packet - _tx_last_packet)); - } - return ERR_OK; -} - -int8_t AsyncClient::_recv(tcp_pcb * pcb, pbuf * pb, int8_t err) { - while (pb != NULL) { - _rx_last_packet = millis(); - //we should not ack before we assimilate the data - _ack_pcb = true; - pbuf * b = pb; - pb = b->next; - b->next = NULL; - if (_pb_cb) { - _pb_cb(_pb_cb_arg, this, b); - } else { - if (_recv_cb) { - _recv_cb(_recv_cb_arg, this, b->payload, b->len); - } - if (!_ack_pcb) { - _rx_ack_len += b->len; - } else if (_pcb) { - _tcp_recved(_pcb, _closed_slot, b->len); - } - } - pbuf_free(b); - } - return ERR_OK; -} - -int8_t AsyncClient::_poll(tcp_pcb * pcb) { - if (!_pcb) { - // log_d("pcb is NULL"); - return ERR_OK; - } - if (pcb != _pcb) { - log_d("0x%08x != 0x%08x", (uint32_t)pcb, (uint32_t)_pcb); - return ERR_OK; - } - - uint32_t now = millis(); - - // ACK Timeout - if (_ack_timeout) { - const uint32_t one_day = 86400000; - bool last_tx_is_after_last_ack = (_rx_last_ack - _tx_last_packet + one_day) < one_day; - if (last_tx_is_after_last_ack && (now - _tx_last_packet) >= _ack_timeout) { - log_d("ack timeout %d", pcb->state); - if (_timeout_cb) - _timeout_cb(_timeout_cb_arg, this, (now - _tx_last_packet)); - return ERR_OK; - } - } - // RX Timeout - if (_rx_timeout && (now - _rx_last_packet) >= (_rx_timeout * 1000)) { - log_d("rx timeout %d", pcb->state); - _close(); - return ERR_OK; - } - // Everything is fine - if (_poll_cb) { - _poll_cb(_poll_cb_arg, this); - } - return ERR_OK; -} - -void AsyncClient::_dns_found(struct ip_addr * ipaddr) { -#if ESP_IDF_VERSION_MAJOR < 5 - if (ipaddr && IP_IS_V4(ipaddr)) { - connect(IPAddress(ip_addr_get_ip4_u32(ipaddr)), _connect_port); -#if LWIP_IPV6 - } else if (ipaddr && ipaddr->u_addr.ip6.addr) { - connect(IPv6Address(ipaddr->u_addr.ip6.addr), _connect_port); -#endif -#else - if (ipaddr) { - IPAddress ip; - ip.from_ip_addr_t(ipaddr); - connect(ip, _connect_port); -#endif - } else { - if (_error_cb) { - _error_cb(_error_cb_arg, this, -55); - } - if (_discard_cb) { - _discard_cb(_discard_cb_arg, this); - } - } -} - -/* - * Public Helper Methods - * */ - -void AsyncClient::stop() { - close(false); -} - -bool AsyncClient::free() { - if (!_pcb) { - return true; - } - if (_pcb->state == CLOSED || _pcb->state > ESTABLISHED) { - return true; - } - return false; -} - -size_t AsyncClient::write(const char * data) { - if (data == NULL) { - return 0; - } - return write(data, strlen(data)); -} - -size_t AsyncClient::write(const char * data, size_t size, uint8_t apiflags) { - size_t will_send = add(data, size, apiflags); - if (!will_send || !send()) { - return 0; - } - return will_send; -} - -void AsyncClient::setRxTimeout(uint32_t timeout) { - _rx_timeout = timeout; -} - -uint32_t AsyncClient::getRxTimeout() { - return _rx_timeout; -} - -uint32_t AsyncClient::getAckTimeout() { - return _ack_timeout; -} - -void AsyncClient::setAckTimeout(uint32_t timeout) { - _ack_timeout = timeout; -} - -void AsyncClient::setNoDelay(bool nodelay) { - if (!_pcb) { - return; - } - if (nodelay) { - tcp_nagle_disable(_pcb); - } else { - tcp_nagle_enable(_pcb); - } -} - -bool AsyncClient::getNoDelay() { - if (!_pcb) { - return false; - } - return tcp_nagle_disabled(_pcb); -} - -void AsyncClient::setKeepAlive(uint32_t ms, uint8_t cnt) { - if (ms != 0) { - _pcb->so_options |= SOF_KEEPALIVE; //Turn on TCP Keepalive for the given pcb - // Set the time between keepalive messages in milli-seconds - _pcb->keep_idle = ms; - _pcb->keep_intvl = ms; - _pcb->keep_cnt = cnt; //The number of unanswered probes required to force closure of the socket - } else { - _pcb->so_options &= ~SOF_KEEPALIVE; //Turn off TCP Keepalive for the given pcb - } -} - -uint16_t AsyncClient::getMss() { - if (!_pcb) { - return 0; - } - return tcp_mss(_pcb); -} - -uint32_t AsyncClient::getRemoteAddress() { - if (!_pcb) { - return 0; - } -#if LWIP_IPV4 && LWIP_IPV6 - return _pcb->remote_ip.u_addr.ip4.addr; -#else - return _pcb->remote_ip.addr; -#endif -} - -#if LWIP_IPV6 -ip6_addr_t AsyncClient::getRemoteAddress6() { - if (!_pcb) { - ip6_addr_t nulladdr; - ip6_addr_set_zero(&nulladdr); - return nulladdr; - } - return _pcb->remote_ip.u_addr.ip6; -} - -ip6_addr_t AsyncClient::getLocalAddress6() { - if (!_pcb) { - ip6_addr_t nulladdr; - ip6_addr_set_zero(&nulladdr); - return nulladdr; - } - return _pcb->local_ip.u_addr.ip6; -} -#if ESP_IDF_VERSION_MAJOR < 5 -IPv6Address AsyncClient::remoteIP6() { - return IPv6Address(getRemoteAddress6().addr); -} - -IPv6Address AsyncClient::localIP6() { - return IPv6Address(getLocalAddress6().addr); -} -#else -IPAddress AsyncClient::remoteIP6() { - if (!_pcb) { - return IPAddress(IPType::IPv6); - } - IPAddress ip; - ip.from_ip_addr_t(&(_pcb->remote_ip)); - return ip; -} - -IPAddress AsyncClient::localIP6() { - if (!_pcb) { - return IPAddress(IPType::IPv6); - } - IPAddress ip; - ip.from_ip_addr_t(&(_pcb->local_ip)); - return ip; -} -#endif -#endif - -uint16_t AsyncClient::getRemotePort() { - if (!_pcb) { - return 0; - } - return _pcb->remote_port; -} - -uint32_t AsyncClient::getLocalAddress() { - if (!_pcb) { - return 0; - } -#if LWIP_IPV4 && LWIP_IPV6 - return _pcb->local_ip.u_addr.ip4.addr; -#else - return _pcb->local_ip.addr; -#endif -} - -uint16_t AsyncClient::getLocalPort() { - if (!_pcb) { - return 0; - } - return _pcb->local_port; -} - -IPAddress AsyncClient::remoteIP() { -#if ESP_IDF_VERSION_MAJOR < 5 - return IPAddress(getRemoteAddress()); -#else - if (!_pcb) { - return IPAddress(); - } - IPAddress ip; - ip.from_ip_addr_t(&(_pcb->remote_ip)); - return ip; -#endif -} - -uint16_t AsyncClient::remotePort() { - return getRemotePort(); -} - -IPAddress AsyncClient::localIP() { -#if ESP_IDF_VERSION_MAJOR < 5 - return IPAddress(getLocalAddress()); -#else - if (!_pcb) { - return IPAddress(); - } - IPAddress ip; - ip.from_ip_addr_t(&(_pcb->local_ip)); - return ip; -#endif -} - - -uint16_t AsyncClient::localPort() { - return getLocalPort(); -} - -uint8_t AsyncClient::state() { - if (!_pcb) { - return 0; - } - return _pcb->state; -} - -bool AsyncClient::connected() { - if (!_pcb) { - return false; - } - return _pcb->state == ESTABLISHED; -} - -bool AsyncClient::connecting() { - if (!_pcb) { - return false; - } - return _pcb->state > CLOSED && _pcb->state < ESTABLISHED; -} - -bool AsyncClient::disconnecting() { - if (!_pcb) { - return false; - } - return _pcb->state > ESTABLISHED && _pcb->state < TIME_WAIT; -} - -bool AsyncClient::disconnected() { - if (!_pcb) { - return true; - } - return _pcb->state == CLOSED || _pcb->state == TIME_WAIT; -} - -bool AsyncClient::freeable() { - if (!_pcb) { - return true; - } - return _pcb->state == CLOSED || _pcb->state > ESTABLISHED; -} - -bool AsyncClient::canSend() { - return space() > 0; -} - -const char * AsyncClient::errorToString(int8_t error) { - switch (error) { - case ERR_OK: - return "OK"; - case ERR_MEM: - return "Out of memory error"; - case ERR_BUF: - return "Buffer error"; - case ERR_TIMEOUT: - return "Timeout"; - case ERR_RTE: - return "Routing problem"; - case ERR_INPROGRESS: - return "Operation in progress"; - case ERR_VAL: - return "Illegal value"; - case ERR_WOULDBLOCK: - return "Operation would block"; - case ERR_USE: - return "Address in use"; - case ERR_ALREADY: - return "Already connected"; - case ERR_CONN: - return "Not connected"; - case ERR_IF: - return "Low-level netif error"; - case ERR_ABRT: - return "Connection aborted"; - case ERR_RST: - return "Connection reset"; - case ERR_CLSD: - return "Connection closed"; - case ERR_ARG: - return "Illegal argument"; - case -55: - return "DNS failed"; - default: - return "UNKNOWN"; - } -} - -const char * AsyncClient::stateToString() { - switch (state()) { - case 0: - return "Closed"; - case 1: - return "Listen"; - case 2: - return "SYN Sent"; - case 3: - return "SYN Received"; - case 4: - return "Established"; - case 5: - return "FIN Wait 1"; - case 6: - return "FIN Wait 2"; - case 7: - return "Close Wait"; - case 8: - return "Closing"; - case 9: - return "Last ACK"; - case 10: - return "Time Wait"; - default: - return "UNKNOWN"; - } -} - -/* - * Static Callbacks (LwIP C2C++ interconnect) - * */ - -void AsyncClient::_s_dns_found(const char * name, struct ip_addr * ipaddr, void * arg) { - reinterpret_cast(arg)->_dns_found(ipaddr); -} - -int8_t AsyncClient::_s_poll(void * arg, struct tcp_pcb * pcb) { - return reinterpret_cast(arg)->_poll(pcb); -} - -int8_t AsyncClient::_s_recv(void * arg, struct tcp_pcb * pcb, struct pbuf * pb, int8_t err) { - return reinterpret_cast(arg)->_recv(pcb, pb, err); -} - -int8_t AsyncClient::_s_fin(void * arg, struct tcp_pcb * pcb, int8_t err) { - return reinterpret_cast(arg)->_fin(pcb, err); -} - -int8_t AsyncClient::_s_lwip_fin(void * arg, struct tcp_pcb * pcb, int8_t err) { - return reinterpret_cast(arg)->_lwip_fin(pcb, err); -} - -int8_t AsyncClient::_s_sent(void * arg, struct tcp_pcb * pcb, uint16_t len) { - return reinterpret_cast(arg)->_sent(pcb, len); -} - -void AsyncClient::_s_error(void * arg, int8_t err) { - reinterpret_cast(arg)->_error(err); -} - -int8_t AsyncClient::_s_connected(void * arg, struct tcp_pcb * pcb, int8_t err) { - return reinterpret_cast(arg)->_connected(pcb, err); -} - -/* - Async TCP Server - */ - -AsyncServer::AsyncServer(IPAddress addr, uint16_t port) - : _port(port) -#if ESP_IDF_VERSION_MAJOR < 5 - , _bind4(true) - , _bind6(false) -#else - , _bind4(addr.type() != IPType::IPv6) - , _bind6(addr.type() == IPType::IPv6) -#endif - , _addr(addr) - , _noDelay(false) - , _pcb(0) - , _connect_cb(0) - , _connect_cb_arg(0) { -} - -#if ESP_IDF_VERSION_MAJOR < 5 -AsyncServer::AsyncServer(IPv6Address addr, uint16_t port) - : _port(port) - , _bind4(false) - , _bind6(true) - , _addr6(addr) - , _noDelay(false) - , _pcb(0) - , _connect_cb(0) - , _connect_cb_arg(0) { -} -#endif - -AsyncServer::AsyncServer(uint16_t port) - : _port(port) - , _bind4(true) - , _bind6(false) - , _addr((uint32_t)IPADDR_ANY) -#if ESP_IDF_VERSION_MAJOR < 5 - , _addr6() -#endif - , _noDelay(false) - , _pcb(0) - , _connect_cb(0) - , _connect_cb_arg(0) { -} - -AsyncServer::~AsyncServer() { - end(); -} - -void AsyncServer::onClient(AcConnectHandler cb, void * arg) { - _connect_cb = cb; - _connect_cb_arg = arg; -} - -void AsyncServer::begin() { - if (_pcb) { - return; - } - - if (!_start_async_task()) { - log_e("failed to start task"); - return; - } - int8_t err; - TCP_MUTEX_LOCK(); - _pcb = tcp_new_ip_type(_bind4 && _bind6 ? IPADDR_TYPE_ANY : (_bind6 ? IPADDR_TYPE_V6 : IPADDR_TYPE_V4)); - TCP_MUTEX_UNLOCK(); - if (!_pcb) { - log_e("_pcb == NULL"); - return; - } - - ip_addr_t local_addr; -#if ESP_IDF_VERSION_MAJOR < 5 - if (_bind6) { // _bind6 && _bind4 both at the same time is not supported on Arduino 2 in this lib API - local_addr.type = IPADDR_TYPE_V6; - memcpy(local_addr.u_addr.ip6.addr, static_cast(_addr6), sizeof(uint32_t) * 4); - } else { - local_addr.type = IPADDR_TYPE_V4; - local_addr.u_addr.ip4.addr = _addr; - } -#else - _addr.to_ip_addr_t(&local_addr); -#endif - err = _tcp_bind(_pcb, &local_addr, _port); - - if (err != ERR_OK) { - _tcp_close(_pcb, -1); - log_e("bind error: %d", err); - return; - } - - static uint8_t backlog = 5; - _pcb = _tcp_listen_with_backlog(_pcb, backlog); - if (!_pcb) { - log_e("listen_pcb == NULL"); - return; - } - TCP_MUTEX_LOCK(); - tcp_arg(_pcb, (void *)this); - tcp_accept(_pcb, &_s_accept); - TCP_MUTEX_UNLOCK(); -} - -void AsyncServer::end() { - if (_pcb) { - TCP_MUTEX_LOCK(); - tcp_arg(_pcb, NULL); - tcp_accept(_pcb, NULL); - if (tcp_close(_pcb) != ERR_OK) { - TCP_MUTEX_UNLOCK(); - _tcp_abort(_pcb, -1); - } else { - TCP_MUTEX_UNLOCK(); - } - _pcb = NULL; - } -} - -//runs on LwIP thread -int8_t AsyncServer::_accept(tcp_pcb * pcb, int8_t err) { - //ets_printf("+A: 0x%08x\n", pcb); - if (_connect_cb) { - AsyncClient * c = new AsyncClient(pcb); - if (c) { - c->setNoDelay(_noDelay); - return _tcp_accept(this, c); - } - } - if (tcp_close(pcb) != ERR_OK) { - tcp_abort(pcb); - } - log_d("FAIL"); - return ERR_OK; -} - -int8_t AsyncServer::_accepted(AsyncClient * client) { - if (_connect_cb) { - _connect_cb(_connect_cb_arg, client); - } - return ERR_OK; -} - -void AsyncServer::setNoDelay(bool nodelay) { - _noDelay = nodelay; -} - -bool AsyncServer::getNoDelay() { - return _noDelay; -} - -uint8_t AsyncServer::status() { - if (!_pcb) { - return 0; - } - return _pcb->state; -} - -int8_t AsyncServer::_s_accept(void * arg, tcp_pcb * pcb, int8_t err) { - return reinterpret_cast(arg)->_accept(pcb, err); -} - -int8_t AsyncServer::_s_accepted(void * arg, AsyncClient * client) { - return reinterpret_cast(arg)->_accepted(client); -} diff --git a/lib/AsyncTCP/src/AsyncTCP.h b/lib/AsyncTCP/src/AsyncTCP.h deleted file mode 100644 index 40a894e1f..000000000 --- a/lib/AsyncTCP/src/AsyncTCP.h +++ /dev/null @@ -1,286 +0,0 @@ -/* - Asynchronous TCP library for Espressif MCUs - - Copyright (c) 2016 Hristo Gochkov. All rights reserved. - This file is part of the esp8266 core 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 -*/ - -#ifndef ASYNCTCP_H_ -#define ASYNCTCP_H_ - -#define ASYNCTCP_VERSION "3.2.14" -#define ASYNCTCP_VERSION_MAJOR 3 -#define ASYNCTCP_VERSION_MINOR 2 -#define ASYNCTCP_VERSION_REVISION 14 -#define ASYNCTCP_FORK_mathieucarbou - -#include "IPAddress.h" -#if ESP_IDF_VERSION_MAJOR < 5 -#include "IPv6Address.h" -#endif -#include -#include "lwip/ip_addr.h" -#include "lwip/ip6_addr.h" - -#ifndef LIBRETINY -#include "sdkconfig.h" -extern "C" { -#include "freertos/semphr.h" -#include "lwip/pbuf.h" -} -#else -extern "C" { -#include -#include -} -#define CONFIG_ASYNC_TCP_RUNNING_CORE -1 //any available core -#define CONFIG_ASYNC_TCP_USE_WDT 0 -#endif - -//If core is not defined, then we are running in Arduino or PIO -#ifndef CONFIG_ASYNC_TCP_RUNNING_CORE -#define CONFIG_ASYNC_TCP_RUNNING_CORE -1 //any available core -// Note default was 1 and previously set to 0 for EMS-ESP -#define CONFIG_ASYNC_TCP_USE_WDT 1 //if enabled, adds between 33us and 200us per event -#endif - -// EMS-ESP: stack usage measured: ESP32: ~2.3K, ESP32S3: ~3.5k -#ifndef CONFIG_ASYNC_TCP_STACK_SIZE -#define CONFIG_ASYNC_TCP_STACK_SIZE 6144 -#endif - -#ifndef CONFIG_ASYNC_TCP_PRIORITY -#define CONFIG_ASYNC_TCP_PRIORITY 5 -#endif - -// EMS-ESP: maybe enlarge queue to 64 or 128 see https://github.com/emsesp/EMS-ESP32/issues/177 -#ifndef CONFIG_ASYNC_TCP_QUEUE_SIZE -#define CONFIG_ASYNC_TCP_QUEUE_SIZE 32 -#endif - -#ifndef CONFIG_ASYNC_TCP_MAX_ACK_TIME -#define CONFIG_ASYNC_TCP_MAX_ACK_TIME 5000 -#endif - -class AsyncClient; - -#define ASYNC_WRITE_FLAG_COPY 0x01 //will allocate new buffer to hold the data while sending (else will hold reference to the data given) -#define ASYNC_WRITE_FLAG_MORE 0x02 //will not send PSH flag, meaning that there should be more data to be sent before the application should react. - -typedef std::function AcConnectHandler; -typedef std::function AcAckHandler; -typedef std::function AcErrorHandler; -typedef std::function AcDataHandler; -typedef std::function AcPacketHandler; -typedef std::function AcTimeoutHandler; - -struct tcp_pcb; -struct ip_addr; - -class AsyncClient { - public: - AsyncClient(tcp_pcb * pcb = 0); - ~AsyncClient(); - - AsyncClient & operator=(const AsyncClient & other); - AsyncClient & operator+=(const AsyncClient & other); - - bool operator==(const AsyncClient & other); - - bool operator!=(const AsyncClient & other) { - return !(*this == other); - } - bool connect(const IPAddress & ip, uint16_t port); -#if ESP_IDF_VERSION_MAJOR < 5 - bool connect(const IPv6Address & ip, uint16_t port); -#endif - bool connect(const char * host, uint16_t port); - void close(bool now = false); - void stop(); - int8_t abort(); - bool free(); - - bool canSend(); //ack is not pending - size_t space(); //space available in the TCP window - size_t add(const char * data, size_t size, uint8_t apiflags = ASYNC_WRITE_FLAG_COPY); //add for sending - bool send(); //send all data added with the method above - - //write equals add()+send() - size_t write(const char * data); - size_t write(const char * data, size_t size, uint8_t apiflags = ASYNC_WRITE_FLAG_COPY); //only when canSend() == true - - uint8_t state(); - bool connecting(); - bool connected(); - bool disconnecting(); - bool disconnected(); - bool freeable(); //disconnected or disconnecting - - uint16_t getMss(); - - uint32_t getRxTimeout(); - void setRxTimeout(uint32_t timeout); //no RX data timeout for the connection in seconds - - uint32_t getAckTimeout(); - void setAckTimeout(uint32_t timeout); //no ACK timeout for the last sent packet in milliseconds - - void setNoDelay(bool nodelay); - bool getNoDelay(); - - void setKeepAlive(uint32_t ms, uint8_t cnt); - - uint32_t getRemoteAddress(); - uint16_t getRemotePort(); - uint32_t getLocalAddress(); - uint16_t getLocalPort(); -#if LWIP_IPV6 - ip6_addr_t getRemoteAddress6(); - ip6_addr_t getLocalAddress6(); -#if ESP_IDF_VERSION_MAJOR < 5 - IPv6Address remoteIP6(); - IPv6Address localIP6(); -#else - IPAddress remoteIP6(); - IPAddress localIP6(); -#endif -#endif - - //compatibility - IPAddress remoteIP(); - uint16_t remotePort(); - IPAddress localIP(); - uint16_t localPort(); - - void onConnect(AcConnectHandler cb, void * arg = 0); //on successful connect - void onDisconnect(AcConnectHandler cb, void * arg = 0); //disconnected - void onAck(AcAckHandler cb, void * arg = 0); //ack received - void onError(AcErrorHandler cb, void * arg = 0); //unsuccessful connect or error - void onData(AcDataHandler cb, void * arg = 0); //data received (called if onPacket is not used) - void onPacket(AcPacketHandler cb, void * arg = 0); //data received - void onTimeout(AcTimeoutHandler cb, void * arg = 0); //ack timeout - void onPoll(AcConnectHandler cb, void * arg = 0); //every 125ms when connected - - void ackPacket(struct pbuf * pb); //ack pbuf from onPacket - size_t ack(size_t len); //ack data that you have not acked using the method below - void ackLater() { - _ack_pcb = false; - } //will not ack the current packet. Call from onData - - const char * errorToString(int8_t error); - const char * stateToString(); - - //Do not use any of the functions below! - static int8_t _s_poll(void * arg, struct tcp_pcb * tpcb); - static int8_t _s_recv(void * arg, struct tcp_pcb * tpcb, struct pbuf * pb, int8_t err); - static int8_t _s_fin(void * arg, struct tcp_pcb * tpcb, int8_t err); - static int8_t _s_lwip_fin(void * arg, struct tcp_pcb * tpcb, int8_t err); - static void _s_error(void * arg, int8_t err); - static int8_t _s_sent(void * arg, struct tcp_pcb * tpcb, uint16_t len); - static int8_t _s_connected(void * arg, struct tcp_pcb * tpcb, int8_t err); - static void _s_dns_found(const char * name, struct ip_addr * ipaddr, void * arg); - - int8_t _recv(tcp_pcb * pcb, pbuf * pb, int8_t err); - tcp_pcb * pcb() { - return _pcb; - } - - protected: - bool _connect(ip_addr_t addr, uint16_t port); - - tcp_pcb * _pcb; - int8_t _closed_slot; - - AcConnectHandler _connect_cb; - void * _connect_cb_arg; - AcConnectHandler _discard_cb; - void * _discard_cb_arg; - AcAckHandler _sent_cb; - void * _sent_cb_arg; - AcErrorHandler _error_cb; - void * _error_cb_arg; - AcDataHandler _recv_cb; - void * _recv_cb_arg; - AcPacketHandler _pb_cb; - void * _pb_cb_arg; - AcTimeoutHandler _timeout_cb; - void * _timeout_cb_arg; - AcConnectHandler _poll_cb; - void * _poll_cb_arg; - - bool _ack_pcb; - uint32_t _tx_last_packet; - uint32_t _rx_ack_len; - uint32_t _rx_last_packet; - uint32_t _rx_timeout; - uint32_t _rx_last_ack; - uint32_t _ack_timeout; - uint16_t _connect_port; - - int8_t _close(); - void _free_closed_slot(); - bool _allocate_closed_slot(); - int8_t _connected(tcp_pcb * pcb, int8_t err); - void _error(int8_t err); - int8_t _poll(tcp_pcb * pcb); - int8_t _sent(tcp_pcb * pcb, uint16_t len); - int8_t _fin(tcp_pcb * pcb, int8_t err); - int8_t _lwip_fin(tcp_pcb * pcb, int8_t err); - void _dns_found(struct ip_addr * ipaddr); - - public: - AsyncClient * prev; - AsyncClient * next; -}; - -class AsyncServer { - public: - AsyncServer(IPAddress addr, uint16_t port); -#if ESP_IDF_VERSION_MAJOR < 5 - AsyncServer(IPv6Address addr, uint16_t port); -#endif - AsyncServer(uint16_t port); - ~AsyncServer(); - void onClient(AcConnectHandler cb, void * arg); - void begin(); - void end(); - void setNoDelay(bool nodelay); - bool getNoDelay(); - uint8_t status(); - - //Do not use any of the functions below! - static int8_t _s_accept(void * arg, tcp_pcb * newpcb, int8_t err); - static int8_t _s_accepted(void * arg, AsyncClient * client); - - protected: - uint16_t _port; - bool _bind4 = false; - bool _bind6 = false; - IPAddress _addr; -#if ESP_IDF_VERSION_MAJOR < 5 - IPv6Address _addr6; -#endif - bool _noDelay; - tcp_pcb * _pcb; - AcConnectHandler _connect_cb; - void * _connect_cb_arg; - - int8_t _accept(tcp_pcb * newpcb, int8_t err); - int8_t _accepted(AsyncClient * client); -}; - - -#endif /* ASYNCTCP_H_ */ diff --git a/lib/ESPAsyncWebServer/LICENSE b/lib/ESPAsyncWebServer/LICENSE deleted file mode 100644 index 153d416dc..000000000 --- a/lib/ESPAsyncWebServer/LICENSE +++ /dev/null @@ -1,165 +0,0 @@ - GNU LESSER GENERAL PUBLIC LICENSE - Version 3, 29 June 2007 - - Copyright (C) 2007 Free Software Foundation, Inc. - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - - This version of the GNU Lesser General Public License incorporates -the terms and conditions of version 3 of the GNU General Public -License, supplemented by the additional permissions listed below. - - 0. Additional Definitions. - - As used herein, "this License" refers to version 3 of the GNU Lesser -General Public License, and the "GNU GPL" refers to version 3 of the GNU -General Public License. - - "The Library" refers to a covered work governed by this License, -other than an Application or a Combined Work as defined below. - - An "Application" is any work that makes use of an interface provided -by the Library, but which is not otherwise based on the Library. -Defining a subclass of a class defined by the Library is deemed a mode -of using an interface provided by the Library. - - A "Combined Work" is a work produced by combining or linking an -Application with the Library. The particular version of the Library -with which the Combined Work was made is also called the "Linked -Version". - - The "Minimal Corresponding Source" for a Combined Work means the -Corresponding Source for the Combined Work, excluding any source code -for portions of the Combined Work that, considered in isolation, are -based on the Application, and not on the Linked Version. - - The "Corresponding Application Code" for a Combined Work means the -object code and/or source code for the Application, including any data -and utility programs needed for reproducing the Combined Work from the -Application, but excluding the System Libraries of the Combined Work. - - 1. Exception to Section 3 of the GNU GPL. - - You may convey a covered work under sections 3 and 4 of this License -without being bound by section 3 of the GNU GPL. - - 2. Conveying Modified Versions. - - If you modify a copy of the Library, and, in your modifications, a -facility refers to a function or data to be supplied by an Application -that uses the facility (other than as an argument passed when the -facility is invoked), then you may convey a copy of the modified -version: - - a) under this License, provided that you make a good faith effort to - ensure that, in the event an Application does not supply the - function or data, the facility still operates, and performs - whatever part of its purpose remains meaningful, or - - b) under the GNU GPL, with none of the additional permissions of - this License applicable to that copy. - - 3. Object Code Incorporating Material from Library Header Files. - - The object code form of an Application may incorporate material from -a header file that is part of the Library. You may convey such object -code under terms of your choice, provided that, if the incorporated -material is not limited to numerical parameters, data structure -layouts and accessors, or small macros, inline functions and templates -(ten or fewer lines in length), you do both of the following: - - a) Give prominent notice with each copy of the object code that the - Library is used in it and that the Library and its use are - covered by this License. - - b) Accompany the object code with a copy of the GNU GPL and this license - document. - - 4. Combined Works. - - You may convey a Combined Work under terms of your choice that, -taken together, effectively do not restrict modification of the -portions of the Library contained in the Combined Work and reverse -engineering for debugging such modifications, if you also do each of -the following: - - a) Give prominent notice with each copy of the Combined Work that - the Library is used in it and that the Library and its use are - covered by this License. - - b) Accompany the Combined Work with a copy of the GNU GPL and this license - document. - - c) For a Combined Work that displays copyright notices during - execution, include the copyright notice for the Library among - these notices, as well as a reference directing the user to the - copies of the GNU GPL and this license document. - - d) Do one of the following: - - 0) Convey the Minimal Corresponding Source under the terms of this - License, and the Corresponding Application Code in a form - suitable for, and under terms that permit, the user to - recombine or relink the Application with a modified version of - the Linked Version to produce a modified Combined Work, in the - manner specified by section 6 of the GNU GPL for conveying - Corresponding Source. - - 1) Use a suitable shared library mechanism for linking with the - Library. A suitable mechanism is one that (a) uses at run time - a copy of the Library already present on the user's computer - system, and (b) will operate properly with a modified version - of the Library that is interface-compatible with the Linked - Version. - - e) Provide Installation Information, but only if you would otherwise - be required to provide such information under section 6 of the - GNU GPL, and only to the extent that such information is - necessary to install and execute a modified version of the - Combined Work produced by recombining or relinking the - Application with a modified version of the Linked Version. (If - you use option 4d0, the Installation Information must accompany - the Minimal Corresponding Source and Corresponding Application - Code. If you use option 4d1, you must provide the Installation - Information in the manner specified by section 6 of the GNU GPL - for conveying Corresponding Source.) - - 5. Combined Libraries. - - You may place library facilities that are a work based on the -Library side by side in a single library together with other library -facilities that are not Applications and are not covered by this -License, and convey such a combined library under terms of your -choice, if you do both of the following: - - a) Accompany the combined library with a copy of the same work based - on the Library, uncombined with any other library facilities, - conveyed under the terms of this License. - - b) Give prominent notice with the combined library that part of it - is a work based on the Library, and explaining where to find the - accompanying uncombined form of the same work. - - 6. Revised Versions of the GNU Lesser General Public License. - - The Free Software Foundation may publish revised and/or new versions -of the GNU Lesser General Public License from time to time. Such new -versions will be similar in spirit to the present version, but may -differ in detail to address new problems or concerns. - - Each version is given a distinguishing version number. If the -Library as you received it specifies that a certain numbered version -of the GNU Lesser General Public License "or any later version" -applies to it, you have the option of following the terms and -conditions either of that published version or of any later version -published by the Free Software Foundation. If the Library as you -received it does not specify a version number of the GNU Lesser -General Public License, you may choose any version of the GNU Lesser -General Public License ever published by the Free Software Foundation. - - If the Library as you received it specifies that a proxy can decide -whether future versions of the GNU Lesser General Public License shall -apply, that proxy's public statement of acceptance of any version is -permanent authorization for you to choose that version for the -Library. \ No newline at end of file diff --git a/lib/ESPAsyncWebServer/README.md b/lib/ESPAsyncWebServer/README.md deleted file mode 100644 index 8038a041d..000000000 --- a/lib/ESPAsyncWebServer/README.md +++ /dev/null @@ -1,75 +0,0 @@ -# ESP Async WebServer - -[![License: LGPL 3.0](https://img.shields.io/badge/License-LGPL%203.0-yellow.svg)](https://opensource.org/license/lgpl-3-0/) -[![Continuous Integration](https://github.com/mathieucarbou/ESPAsyncWebServer/actions/workflows/ci.yml/badge.svg)](https://github.com/mathieucarbou/ESPAsyncWebServer/actions/workflows/ci.yml) -[![PlatformIO Registry](https://badges.registry.platformio.org/packages/mathieucarbou/library/ESP%20Async%20WebServer.svg)](https://registry.platformio.org/libraries/mathieucarbou/ESP%20Async%20WebServer) - -Async Web Server for ESP31B - -This is using - -This fork is based on and includes all the concurrency fixes. - -## Changes - -- SPIFFSEditor is removed -- Arduino Json 7 compatibility -- Deployed in PlatformIO registry and Arduino IDE library manager -- CI -- Only supports ESP32 -- Resurrected `AsyncWebSocketMessageBuffer` and `makeBuffer()` in order to make the fork API-compatible with the original library from me-no-dev regarding WebSocket. - -## Documentation - -Usage and API stays the same as the original library. -Please look at the original libraries for more examples and documentation. - -[https://github.com/yubox-node-org/ESPAsyncWebServer](https://github.com/yubox-node-org/ESPAsyncWebServer) - -## `AsyncWebSocketMessageBuffer` and `makeBuffer()` - -The fork from `yubox-node-org` introduces some breaking API changes compared to the original library, especially regarding the use of `std::shared_ptr>` for WebSocket. - -This fork is compatible with the original library from `me-no-dev` regarding WebSocket, and wraps the optimizations done by `yubox-node-org` in the `AsyncWebSocketMessageBuffer` class. -So you have the choice of which API to use. -I strongly suggest to use the optimized API from `yubox-node-org` as it is much more efficient. - -Here is an example for serializing a Json document in a websocket message buffer. This code is compatible with any forks, but not optimized: - -```cpp -void send(JsonDocument& doc) { - const size_t len = measureJson(doc); - - // original API from me-no-dev - AsyncWebSocketMessageBuffer* buffer = _ws->makeBuffer(len); - assert(buffer); // up to you to keep or remove this - serializeJson(doc, buffer->get(), len); - _ws->textAll(buffer); -} -``` - -Here is an example for serializing a Json document in a more optimized way, and compatible with both forks: - -```cpp -void send(JsonDocument& doc) { - const size_t len = measureJson(doc); - -#if defined(ASYNCWEBSERVER_FORK_mathieucarbou) - - // this fork (originally from yubox-node-org), uses another API with shared pointer that better support concurrent use cases then the original project - auto buffer = std::make_shared>(len); - assert(buffer); // up to you to keep or remove this - serializeJson(doc, buffer->data(), len); - _ws->textAll(std::move(buffer)); - -#else - - // original API from me-no-dev - AsyncWebSocketMessageBuffer* buffer = _ws->makeBuffer(len); - assert(buffer); // up to you to keep or remove this - serializeJson(doc, buffer->get(), len); - _ws->textAll(buffer); - -#endif -} -``` diff --git a/lib/ESPAsyncWebServer/src/AsyncEventSource.cpp b/lib/ESPAsyncWebServer/src/AsyncEventSource.cpp deleted file mode 100644 index 69a0253ea..000000000 --- a/lib/ESPAsyncWebServer/src/AsyncEventSource.cpp +++ /dev/null @@ -1,405 +0,0 @@ -/* - Asynchronous WebServer library for Espressif MCUs - - Copyright (c) 2016 Hristo Gochkov. 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 -*/ -#include "Arduino.h" -#include "AsyncEventSource.h" - -static String generateEventMessage(const char *message, const char *event, uint32_t id, uint32_t reconnect){ - String ev = ""; - - if(reconnect){ - ev += "retry: "; - ev += String(reconnect); - ev += "\r\n"; - } - - if(id){ - ev += "id: "; - ev += String(id); - ev += "\r\n"; - } - - if(event != NULL){ - ev += "event: "; - ev += String(event); - ev += "\r\n"; - } - - if(message != NULL){ - size_t messageLen = strlen(message); - char * lineStart = (char *)message; - char * lineEnd; - do { - char * nextN = strchr(lineStart, '\n'); - char * nextR = strchr(lineStart, '\r'); - if(nextN == NULL && nextR == NULL){ - size_t llen = ((char *)message + messageLen) - lineStart; - char * ldata = (char *)malloc(llen+1); - if(ldata != NULL){ - memcpy(ldata, lineStart, llen); - ldata[llen] = 0; - ev += "data: "; - ev += ldata; - ev += "\r\n\r\n"; - free(ldata); - } - lineStart = (char *)message + messageLen; - } else { - char * nextLine = NULL; - if(nextN != NULL && nextR != NULL){ - if(nextR < nextN){ - lineEnd = nextR; - if(nextN == (nextR + 1)) - nextLine = nextN + 1; - else - nextLine = nextR + 1; - } else { - lineEnd = nextN; - if(nextR == (nextN + 1)) - nextLine = nextR + 1; - else - nextLine = nextN + 1; - } - } else if(nextN != NULL){ - lineEnd = nextN; - nextLine = nextN + 1; - } else { - lineEnd = nextR; - nextLine = nextR + 1; - } - - size_t llen = lineEnd - lineStart; - char * ldata = (char *)malloc(llen+1); - if(ldata != NULL){ - memcpy(ldata, lineStart, llen); - ldata[llen] = 0; - ev += "data: "; - ev += ldata; - ev += "\r\n"; - free(ldata); - } - lineStart = nextLine; - if(lineStart == ((char *)message + messageLen)) - ev += "\r\n"; - } - } while(lineStart < ((char *)message + messageLen)); - } - - return ev; -} - -// Message - -AsyncEventSourceMessage::AsyncEventSourceMessage(const char * data, size_t len) -: _data(nullptr), _len(len), _sent(0), _acked(0) -{ - _data = (uint8_t*)malloc(_len+1); - if(_data == nullptr){ - _len = 0; - } else { - memcpy(_data, data, len); - _data[_len] = 0; - } -} - -AsyncEventSourceMessage::~AsyncEventSourceMessage() { - if(_data != NULL) - free(_data); -} - -size_t AsyncEventSourceMessage::ack(size_t len) { - // If the whole message is now acked... - if(_acked + len > _len){ - // Return the number of extra bytes acked (they will be carried on to the next message) - const size_t extra = _acked + len - _len; - _acked = _len; - return extra; - } - // Return that no extra bytes left. - _acked += len; - return 0; -} - -size_t AsyncEventSourceMessage::write_buffer(AsyncClient *client) { - if (!client->canSend()) - return 0; - const size_t len = _len - _sent; - if(client->space() < len){ - return 0; - } - size_t sent = client->add((const char *)_data, len); - _sent += sent; - return sent; -} - -size_t AsyncEventSourceMessage::send(AsyncClient *client) { - size_t sent = write_buffer(client); - client->send(); - return sent; -} - -// Client - -AsyncEventSourceClient::AsyncEventSourceClient(AsyncWebServerRequest *request, AsyncEventSource *server) -: _messageQueue(LinkedList([](AsyncEventSourceMessage *m){ delete m; })) -{ - _client = request->client(); - _server = server; - _lastId = 0; - if(request->hasHeader("Last-Event-ID")) - _lastId = atoi(request->getHeader("Last-Event-ID")->value().c_str()); - - _client->setRxTimeout(0); - _client->onError(NULL, NULL); - _client->onAck([](void *r, AsyncClient* c, size_t len, uint32_t time){ (void)c; ((AsyncEventSourceClient*)(r))->_onAck(len, time); }, this); - _client->onPoll([](void *r, AsyncClient* c){ (void)c; ((AsyncEventSourceClient*)(r))->_onPoll(); }, this); - _client->onData(NULL, NULL); - _client->onTimeout([this](void *r, AsyncClient* c __attribute__((unused)), uint32_t time){ ((AsyncEventSourceClient*)(r))->_onTimeout(time); }, this); - _client->onDisconnect([this](void *r, AsyncClient* c){ ((AsyncEventSourceClient*)(r))->_onDisconnect(); delete c; }, this); - - _server->_addClient(this); - delete request; - - _client->setNoDelay(true); -} - -AsyncEventSourceClient::~AsyncEventSourceClient(){ - _messageQueue.free(); - close(); -} - -void AsyncEventSourceClient::_queueMessage(AsyncEventSourceMessage *dataMessage){ - if(dataMessage == NULL) - return; - if(!connected()){ - delete dataMessage; - return; - } - - if(_messageQueue.length() >= SSE_MAX_QUEUED_MESSAGES){ - ets_printf("AsyncEventSourceClient: ERROR: Queue is full, communications too slow, dropping event"); - delete dataMessage; - } else { - _messageQueue.add(dataMessage); - } - if(_client->canSend()) - _runQueue(); -} - -void AsyncEventSourceClient::_onAck(size_t len, uint32_t time){ - _runQueue(); -} - -void AsyncEventSourceClient::_onPoll(){ - if(!_messageQueue.isEmpty()){ - _runQueue(); - } -} - - -void AsyncEventSourceClient::_onTimeout(uint32_t time __attribute__((unused))){ - _client->close(true); -} - -void AsyncEventSourceClient::_onDisconnect(){ - _client = NULL; - _server->_handleDisconnect(this); -} - -void AsyncEventSourceClient::close(){ - if(_client != NULL) - _client->close(); -} - -void AsyncEventSourceClient::write(const char * message, size_t len){ - _queueMessage(new AsyncEventSourceMessage(message, len)); -} - -void AsyncEventSourceClient::send(const char *message, const char *event, uint32_t id, uint32_t reconnect){ - String ev = generateEventMessage(message, event, id, reconnect); - _queueMessage(new AsyncEventSourceMessage(ev.c_str(), ev.length())); -} - -void AsyncEventSourceClient::_runQueue(){ -#if defined(ESP32) - if(!this->_messageQueue_mutex.try_lock()) { - return; - } -#else - if(this->_messageQueue_processing){ - return; - } - this->_messageQueue_processing = true; -#endif // ESP32 - - size_t total_bytes_written = 0; - for(auto i = _messageQueue.begin(); i != _messageQueue.end(); ++i) - { - if(!(*i)->sent()) { - size_t bytes_written = (*i)->write_buffer(_client); - total_bytes_written += bytes_written; - if(bytes_written == 0) - break; - // todo: there is a further optimization to write a partial event to squeeze the last few bytes into the outgoing tcp send buffer, in - // fact all of this code is already set up to do so, it's only write_buffer that needs to be updated to allow it instead of - // returning zero when the full event won't fit into what's left of the buffer - // todo: windows is taking 40-50ms to send an ack back while it waits for more data which won't come since this code must wait for ack first - // due to system resource limitations - if the dashboard javascript just sends a single byte back per event received (which this - // code would of course throw away as meaningless) then windows (or whatever other host runs the webbrower) will piggyback an ack - // onto that outgoing packet for us, reducing roundtrip ack latency and potentially as much as trippling throughput again - // (measured: ESP-01: 20ms to send another packet after ack received, windows: 40-50ms to ack after receiving a packet) - } - } - if(total_bytes_written > 0) - _client->send(); - - size_t len = total_bytes_written; - while(len && !_messageQueue.isEmpty()){ - len = _messageQueue.front()->ack(len); - if(_messageQueue.front()->finished()){ - _messageQueue.remove(_messageQueue.front()); - } - } - -#if defined(ESP32) - this->_messageQueue_mutex.unlock(); -#else - this->_messageQueue_processing = false; -#endif // ESP32 -} - - -// Handler - -AsyncEventSource::AsyncEventSource(const String& url) - : _url(url) - , _clients(LinkedList([](AsyncEventSourceClient *c){ delete c; })) - , _connectcb(NULL) -{} - -AsyncEventSource::~AsyncEventSource(){ - close(); -} - -void AsyncEventSource::onConnect(ArEventHandlerFunction cb){ - _connectcb = cb; -} - -void AsyncEventSource::_addClient(AsyncEventSourceClient * client){ - /*char * temp = (char *)malloc(2054); - if(temp != NULL){ - memset(temp+1,' ',2048); - temp[0] = ':'; - temp[2049] = '\r'; - temp[2050] = '\n'; - temp[2051] = '\r'; - temp[2052] = '\n'; - temp[2053] = 0; - client->write((const char *)temp, 2053); - free(temp); - }*/ - - _clients.add(client); - if(_connectcb) - _connectcb(client); -} - -void AsyncEventSource::_handleDisconnect(AsyncEventSourceClient * client){ - _clients.remove(client); -} - -void AsyncEventSource::close(){ - for(const auto &c: _clients){ - if(c->connected()) - c->close(); - } -} - -// pmb fix -size_t AsyncEventSource::avgPacketsWaiting() const { - if(_clients.isEmpty()) - return 0; - - size_t aql=0; - uint32_t nConnectedClients=0; - - for(const auto &c: _clients){ - if(c->connected()) { - aql+=c->packetsWaiting(); - ++nConnectedClients; - } - } -// return aql / nConnectedClients; - return ((aql) + (nConnectedClients/2))/(nConnectedClients); // round up -} - -void AsyncEventSource::send(const char *message, const char *event, uint32_t id, uint32_t reconnect){ - - - String ev = generateEventMessage(message, event, id, reconnect); - for(const auto &c: _clients){ - if(c->connected()) { - c->write(ev.c_str(), ev.length()); - } - } -} - -size_t AsyncEventSource::count() const { - return _clients.count_if([](AsyncEventSourceClient *c){ - return c->connected(); - }); -} - -bool AsyncEventSource::canHandle(AsyncWebServerRequest *request){ - if(request->method() != HTTP_GET || !request->url().equals(_url)) { - return false; - } - request->addInterestingHeader("Last-Event-ID"); - return true; -} - -void AsyncEventSource::handleRequest(AsyncWebServerRequest *request){ - if((_username != "" && _password != "") && !request->authenticate(_username.c_str(), _password.c_str())) - return request->requestAuthentication(); - request->send(new AsyncEventSourceResponse(this)); -} - -// Response - -AsyncEventSourceResponse::AsyncEventSourceResponse(AsyncEventSource *server){ - _server = server; - _code = 200; - _contentType = "text/event-stream"; - _sendContentLength = false; - addHeader("Cache-Control", "no-cache"); - addHeader("Connection","keep-alive"); -} - -void AsyncEventSourceResponse::_respond(AsyncWebServerRequest *request){ - String out = _assembleHead(request->version()); - request->client()->write(out.c_str(), _headLength); - _state = RESPONSE_WAIT_ACK; -} - -size_t AsyncEventSourceResponse::_ack(AsyncWebServerRequest *request, size_t len, uint32_t time __attribute__((unused))){ - if(len){ - new AsyncEventSourceClient(request, _server); - } - return 0; -} diff --git a/lib/ESPAsyncWebServer/src/AsyncEventSource.h b/lib/ESPAsyncWebServer/src/AsyncEventSource.h deleted file mode 100644 index e52d91bae..000000000 --- a/lib/ESPAsyncWebServer/src/AsyncEventSource.h +++ /dev/null @@ -1,147 +0,0 @@ -/* - Asynchronous WebServer library for Espressif MCUs - - Copyright (c) 2016 Hristo Gochkov. 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 -*/ -#ifndef ASYNCEVENTSOURCE_H_ -#define ASYNCEVENTSOURCE_H_ - -#include -#include -#if defined(ESP32) || defined(LIBRETINY) -#include -#else -#include -#endif - -#if defined(ESP32) -#include -#endif // ESP32 - -#ifndef SSE_MAX_QUEUED_MESSAGES -#define SSE_MAX_QUEUED_MESSAGES 32 -#endif - -#include - -#include "AsyncWebSynchronization.h" - -#ifdef ESP8266 -#include -#ifdef CRYPTO_HASH_h // include Hash.h from espressif framework if the first include was from the crypto library -#include <../src/Hash.h> -#endif -#endif - -#if defined(ESP32) || defined(LIBRETINY) -#define DEFAULT_MAX_SSE_CLIENTS 8 -#else -#define DEFAULT_MAX_SSE_CLIENTS 4 -#endif - -class AsyncEventSource; -class AsyncEventSourceResponse; -class AsyncEventSourceClient; -typedef std::function ArEventHandlerFunction; - -class AsyncEventSourceMessage { - private: - uint8_t * _data; - size_t _len; - size_t _sent; - //size_t _ack; - size_t _acked; - public: - AsyncEventSourceMessage(const char * data, size_t len); - ~AsyncEventSourceMessage(); - size_t ack(size_t len); - size_t write_buffer(AsyncClient *client); - size_t send(AsyncClient *client); - bool finished(){ return _acked == _len; } - bool sent() { return _sent == _len; } -}; - -class AsyncEventSourceClient { - private: - AsyncClient *_client; - AsyncEventSource *_server; - uint32_t _lastId; -#if defined(ESP32) - std::mutex _messageQueue_mutex; -#else - bool _messageQueue_processing{false}; -#endif // ESP32 - LinkedList _messageQueue; - void _queueMessage(AsyncEventSourceMessage *dataMessage); - void _runQueue(); - - public: - - AsyncEventSourceClient(AsyncWebServerRequest *request, AsyncEventSource *server); - ~AsyncEventSourceClient(); - - AsyncClient* client(){ return _client; } - void close(); - void write(const char * message, size_t len); - void send(const char *message, const char *event=NULL, uint32_t id=0, uint32_t reconnect=0); - bool connected() const { return (_client != NULL) && _client->connected(); } - uint32_t lastId() const { return _lastId; } - size_t packetsWaiting() const { return _messageQueue.length(); } - - //system callbacks (do not call) - void _onAck(size_t len, uint32_t time); - void _onPoll(); - void _onTimeout(uint32_t time); - void _onDisconnect(); -}; - -class AsyncEventSource: public AsyncWebHandler { - private: - String _url; - LinkedList _clients; - ArEventHandlerFunction _connectcb; - public: - AsyncEventSource(const String& url); - ~AsyncEventSource(); - - const char * url() const { return _url.c_str(); } - void close(); - void onConnect(ArEventHandlerFunction cb); - void send(const char *message, const char *event=NULL, uint32_t id=0, uint32_t reconnect=0); - size_t count() const; //number clinets connected - size_t avgPacketsWaiting() const; - - //system callbacks (do not call) - void _addClient(AsyncEventSourceClient * client); - void _handleDisconnect(AsyncEventSourceClient * client); - virtual bool canHandle(AsyncWebServerRequest *request) override final; - virtual void handleRequest(AsyncWebServerRequest *request) override final; -}; - -class AsyncEventSourceResponse: public AsyncWebServerResponse { - private: - String _content; - AsyncEventSource *_server; - public: - AsyncEventSourceResponse(AsyncEventSource *server); - void _respond(AsyncWebServerRequest *request); - size_t _ack(AsyncWebServerRequest *request, size_t len, uint32_t time); - bool _sourceValid() const { return true; } -}; - - -#endif /* ASYNCEVENTSOURCE_H_ */ \ No newline at end of file diff --git a/lib/ESPAsyncWebServer/src/AsyncJson.h b/lib/ESPAsyncWebServer/src/AsyncJson.h deleted file mode 100644 index 0cb74acf3..000000000 --- a/lib/ESPAsyncWebServer/src/AsyncJson.h +++ /dev/null @@ -1,200 +0,0 @@ -// AsyncJson.h - -#ifndef ASYNC_JSON_H_ -#define ASYNC_JSON_H_ - -#include -#include -#include - -constexpr const char * JSON_MIMETYPE = "application/json"; -constexpr const char * MSGPACK_MIMETYPE = "application/msgpack"; - -class ChunkPrint : public Print { - private: - uint8_t * _destination; - size_t _to_skip; - size_t _to_write; - size_t _pos; - - public: - ChunkPrint(uint8_t * destination, size_t from, size_t len) - : _destination(destination) - , _to_skip(from) - , _to_write(len) - , _pos{0} { - } - virtual ~ChunkPrint() { - } - size_t write(uint8_t c) { - if (_to_skip > 0) { - _to_skip--; - return 1; - } else if (_to_write > 0) { - _to_write--; - _destination[_pos++] = c; - return 1; - } - return 0; - } - size_t write(const uint8_t * buffer, size_t size) { - return this->Print::write(buffer, size); - } -}; - -// added msgPack by proddy for EMS-ESP -class AsyncJsonResponse : public AsyncAbstractResponse { - protected: - JsonDocument _jsonBuffer; - JsonVariant _root; - bool _isValid; - bool _isMsgPack; - - public: - AsyncJsonResponse(bool isArray = false, bool isMsgPack = false) - : _isValid{false} - , _isMsgPack{isMsgPack} { - _code = 200; - _contentType = (isMsgPack) ? MSGPACK_MIMETYPE : JSON_MIMETYPE; - if (isArray) - _root = _jsonBuffer.add(); - else - _root = _jsonBuffer.add(); - } - - ~AsyncJsonResponse() { - } - JsonVariant getRoot() { - return _root; - } - bool _sourceValid() const { - return _isValid; - } - size_t setLength() { - _contentLength = _isMsgPack ? measureMsgPack(_root) : measureJson(_root); - - if (_contentLength) { - _isValid = true; - } - return _contentLength; - } - - size_t getSize() { - return _jsonBuffer.size(); - } - - size_t _fillBuffer(uint8_t * data, size_t len) { - ChunkPrint dest(data, _sentLength, len); - _isMsgPack ? serializeMsgPack(_root, dest) : serializeJson(_root, dest); - return len; - } -}; - -// class PrettyAsyncJsonResponse : public AsyncJsonResponse { -// public: -// PrettyAsyncJsonResponse(bool isArray = false) -// : AsyncJsonResponse{isArray} { -// } -// size_t setLength() { -// _contentLength = measureJsonPretty(_root); -// if (_contentLength) { -// _isValid = true; -// } -// return _contentLength; -// } -// size_t _fillBuffer(uint8_t * data, size_t len) { -// ChunkPrint dest(data, _sentLength, len); -// serializeJsonPretty(_root, dest); -// return len; -// } -// }; - -typedef std::function ArJsonRequestHandlerFunction; - -class AsyncCallbackJsonWebHandler : public AsyncWebHandler { - private: - protected: - const String _uri; - WebRequestMethodComposite _method; - ArJsonRequestHandlerFunction _onRequest; - size_t _contentLength; - size_t _maxContentLength; - - public: - AsyncCallbackJsonWebHandler(const String & uri, ArJsonRequestHandlerFunction onRequest) - : _uri(uri) - , _method(HTTP_GET | HTTP_POST | HTTP_PUT | HTTP_PATCH) - , _onRequest(onRequest) - , _maxContentLength(16384) { - } - - void setMethod(WebRequestMethodComposite method) { - _method = method; - } - void setMaxContentLength(int maxContentLength) { - _maxContentLength = maxContentLength; - } - void onRequest(ArJsonRequestHandlerFunction fn) { - _onRequest = fn; - } - - - virtual bool canHandle(AsyncWebServerRequest * request) override final { - if (!_onRequest) - return false; - - WebRequestMethodComposite request_method = request->method(); - - if (!(_method & request_method)) - return false; - - if (_uri.length() && (_uri != request->url() && !request->url().startsWith(_uri + "/"))) - return false; - - if (request_method != HTTP_GET && !request->contentType().equalsIgnoreCase(JSON_MIMETYPE)) - return false; - - request->addInterestingHeader("ANY"); - return true; - } - - virtual void handleRequest(AsyncWebServerRequest * request) override final { - if ((_username != "" && _password != "") && !request->authenticate(_username.c_str(), _password.c_str())) - return request->requestAuthentication(); - if (_onRequest) { - if (request->method() == HTTP_GET) { - JsonVariant json; - _onRequest(request, json); - return; - } else if (request->_tempObject != NULL) { - JsonDocument jsonBuffer; - DeserializationError error = deserializeJson(jsonBuffer, (uint8_t *)(request->_tempObject)); - if (!error) { - JsonVariant json = jsonBuffer.as(); - _onRequest(request, json); - return; - } - } - request->send(_contentLength > _maxContentLength ? 413 : 400); - } else { - request->send(500); - } - } - virtual void handleUpload(AsyncWebServerRequest * request, const String & filename, size_t index, uint8_t * data, size_t len, bool final) override final { - } - virtual void handleBody(AsyncWebServerRequest * request, uint8_t * data, size_t len, size_t index, size_t total) override final { - if (_onRequest) { - _contentLength = total; - if (total > 0 && request->_tempObject == NULL && total < _maxContentLength) { - request->_tempObject = malloc(total); - } - if (request->_tempObject != NULL) { - memcpy((uint8_t *)(request->_tempObject) + index, data, len); - } - } - } - virtual bool isRequestHandlerTrivial() override final { - return _onRequest ? false : true; - } -}; -#endif diff --git a/lib/ESPAsyncWebServer/src/AsyncWebSocket.cpp b/lib/ESPAsyncWebServer/src/AsyncWebSocket.cpp deleted file mode 100644 index 3bae20280..000000000 --- a/lib/ESPAsyncWebServer/src/AsyncWebSocket.cpp +++ /dev/null @@ -1,1266 +0,0 @@ -/* - Asynchronous WebServer library for Espressif MCUs - - Copyright (c) 2016 Hristo Gochkov. All rights reserved. - This file is part of the esp8266 core 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 -*/ -#include "Arduino.h" -#include "AsyncWebSocket.h" - -#include - -#include - -#ifndef ESP8266 -#if ESP_IDF_VERSION_MAJOR < 5 -#include "./port/SHA1Builder.h" -#else -#include -#endif -#include -#else -#include -#endif - -#define MAX_PRINTF_LEN 64 - -size_t webSocketSendFrameWindow(AsyncClient * client) { - if (!client->canSend()) - return 0; - size_t space = client->space(); - if (space < 9) - return 0; - return space - 8; -} - -size_t webSocketSendFrame(AsyncClient * client, bool final, uint8_t opcode, bool mask, uint8_t * data, size_t len) { - if (!client->canSend()) { - // Serial.println("SF 1"); - return 0; - } - size_t space = client->space(); - if (space < 2) { - // Serial.println("SF 2"); - return 0; - } - uint8_t mbuf[4] = {0, 0, 0, 0}; - uint8_t headLen = 2; - if (len && mask) { - headLen += 4; - mbuf[0] = rand() % 0xFF; - mbuf[1] = rand() % 0xFF; - mbuf[2] = rand() % 0xFF; - mbuf[3] = rand() % 0xFF; - } - if (len > 125) - headLen += 2; - if (space < headLen) { - // Serial.println("SF 2"); - return 0; - } - space -= headLen; - - if (len > space) - len = space; - - uint8_t * buf = (uint8_t *)malloc(headLen); - if (buf == NULL) { - //os_printf("could not malloc %u bytes for frame header\n", headLen); - // Serial.println("SF 3"); - return 0; - } - - buf[0] = opcode & 0x0F; - if (final) - buf[0] |= 0x80; - if (len < 126) - buf[1] = len & 0x7F; - else { - buf[1] = 126; - buf[2] = (uint8_t)((len >> 8) & 0xFF); - buf[3] = (uint8_t)(len & 0xFF); - } - if (len && mask) { - buf[1] |= 0x80; - memcpy(buf + (headLen - 4), mbuf, 4); - } - if (client->add((const char *)buf, headLen) != headLen) { - //os_printf("error adding %lu header bytes\n", headLen); - free(buf); - // Serial.println("SF 4"); - return 0; - } - free(buf); - - if (len) { - if (len && mask) { - size_t i; - for (i = 0; i < len; i++) - data[i] = data[i] ^ mbuf[i % 4]; - } - if (client->add((const char *)data, len) != len) { - //os_printf("error adding %lu data bytes\n", len); - // Serial.println("SF 5"); - return 0; - } - } - if (!client->send()) { - //os_printf("error sending frame: %lu\n", headLen+len); - // Serial.println("SF 6"); - return 0; - } - // Serial.println("SF"); - return len; -} - - - -/* - * AsyncWebSocketMessageBuffer - */ - -AsyncWebSocketMessageBuffer::AsyncWebSocketMessageBuffer() - : _buffer(std::make_shared>(0)) { -} - -AsyncWebSocketMessageBuffer::AsyncWebSocketMessageBuffer(uint8_t * data, size_t size) - : _buffer(std::make_shared>(size)) { - if (_buffer->capacity() < size) { - _buffer.reset(); - _buffer = std::make_shared>(0); - } else { - std::memcpy(_buffer->data(), data, size); - } -} - -AsyncWebSocketMessageBuffer::AsyncWebSocketMessageBuffer(size_t size) - : _buffer(std::make_shared>(size)) { - if (_buffer->capacity() < size) { - _buffer.reset(); - _buffer = std::make_shared>(0); - } -} - -AsyncWebSocketMessageBuffer::~AsyncWebSocketMessageBuffer() { - _buffer.reset(); -} - -bool AsyncWebSocketMessageBuffer::reserve(size_t size) { - if (_buffer->capacity() >= size) - return true; - _buffer->reserve(size); - return _buffer->capacity() >= size; -} - -/* - * Control Frame - */ - -class AsyncWebSocketControl { - private: - uint8_t _opcode; - uint8_t * _data; - size_t _len; - bool _mask; - bool _finished; - - public: - AsyncWebSocketControl(uint8_t opcode, const uint8_t * data = NULL, size_t len = 0, bool mask = false) - : _opcode(opcode) - , _len(len) - , _mask(len && mask) - , _finished(false) { - if (data == NULL) - _len = 0; - if (_len) { - if (_len > 125) - _len = 125; - - _data = (uint8_t *)malloc(_len); - - if (_data == NULL) - _len = 0; - else - memcpy(_data, data, len); - } else - _data = NULL; - } - - virtual ~AsyncWebSocketControl() { - if (_data != NULL) - free(_data); - } - - virtual bool finished() const { - return _finished; - } - uint8_t opcode() { - return _opcode; - } - uint8_t len() { - return _len + 2; - } - size_t send(AsyncClient * client) { - _finished = true; - return webSocketSendFrame(client, true, _opcode & 0x0F, _mask, _data, _len); - } -}; - - -/* - * AsyncWebSocketMessage Message - */ - - -AsyncWebSocketMessage::AsyncWebSocketMessage(std::shared_ptr> buffer, uint8_t opcode, bool mask) - : _WSbuffer{buffer} - , _opcode(opcode & 0x07) - , _mask{mask} - , _status{_WSbuffer ? WS_MSG_SENDING : WS_MSG_ERROR} { -} - -void AsyncWebSocketMessage::ack(size_t len, uint32_t time) { - (void)time; - _acked += len; - if (_sent >= _WSbuffer->size() && _acked >= _ack) { - _status = WS_MSG_SENT; - } - //ets_printf("A: %u\n", len); -} - -size_t AsyncWebSocketMessage::send(AsyncClient * client) { - if (_status != WS_MSG_SENDING) - return 0; - if (_acked < _ack) { - return 0; - } - if (_sent == _WSbuffer->size()) { - if (_acked == _ack) - _status = WS_MSG_SENT; - return 0; - } - if (_sent > _WSbuffer->size()) { - _status = WS_MSG_ERROR; - //ets_printf("E: %u > %u\n", _sent, _WSbuffer->length()); - return 0; - } - - size_t toSend = _WSbuffer->size() - _sent; - size_t window = webSocketSendFrameWindow(client); - - if (window < toSend) { - toSend = window; - } - - _sent += toSend; - _ack += toSend + ((toSend < 126) ? 2 : 4) + (_mask * 4); - - //ets_printf("W: %u %u\n", _sent - toSend, toSend); - - bool final = (_sent == _WSbuffer->size()); - uint8_t * dPtr = (uint8_t *)(_WSbuffer->data() + (_sent - toSend)); - uint8_t opCode = (toSend && _sent == toSend) ? _opcode : (uint8_t)WS_CONTINUATION; - - size_t sent = webSocketSendFrame(client, final, opCode, _mask, dPtr, toSend); - _status = WS_MSG_SENDING; - if (toSend && sent != toSend) { - //ets_printf("E: %u != %u\n", toSend, sent); - _sent -= (toSend - sent); - _ack -= (toSend - sent); - } - //ets_printf("S: %u %u\n", _sent, sent); - return sent; -} - - -/* - * Async WebSocket Client - */ -const char * AWSC_PING_PAYLOAD = "ESPAsyncWebServer-PING"; -const size_t AWSC_PING_PAYLOAD_LEN = 22; - -AsyncWebSocketClient::AsyncWebSocketClient(AsyncWebServerRequest * request, AsyncWebSocket * server) - : _tempObject(NULL) { - _client = request->client(); - _server = server; - _clientId = _server->_getNextId(); - _status = WS_CONNECTED; - _pstate = 0; - _lastMessageTime = millis(); - _keepAlivePeriod = 0; - _client->setRxTimeout(0); - _client->onError( - [](void * r, AsyncClient * c, int8_t error) { - (void)c; - ((AsyncWebSocketClient *)(r))->_onError(error); - }, - this); - _client->onAck( - [](void * r, AsyncClient * c, size_t len, uint32_t time) { - (void)c; - ((AsyncWebSocketClient *)(r))->_onAck(len, time); - }, - this); - _client->onDisconnect( - [](void * r, AsyncClient * c) { - ((AsyncWebSocketClient *)(r))->_onDisconnect(); - delete c; - }, - this); - _client->onTimeout( - [](void * r, AsyncClient * c, uint32_t time) { - (void)c; - ((AsyncWebSocketClient *)(r))->_onTimeout(time); - }, - this); - _client->onData( - [](void * r, AsyncClient * c, void * buf, size_t len) { - (void)c; - ((AsyncWebSocketClient *)(r))->_onData(buf, len); - }, - this); - _client->onPoll( - [](void * r, AsyncClient * c) { - (void)c; - ((AsyncWebSocketClient *)(r))->_onPoll(); - }, - this); - _server->_handleEvent(this, WS_EVT_CONNECT, request, NULL, 0); - delete request; - memset(&_pinfo, 0, sizeof(_pinfo)); -} - -AsyncWebSocketClient::~AsyncWebSocketClient() { - { - AsyncWebLockGuard l(_lock); - - _messageQueue.clear(); - _controlQueue.clear(); - } - _server->_handleEvent(this, WS_EVT_DISCONNECT, NULL, NULL, 0); -} - -void AsyncWebSocketClient::_clearQueue() { - while (!_messageQueue.empty() && _messageQueue.front().finished()) - _messageQueue.pop_front(); -} - -void AsyncWebSocketClient::_onAck(size_t len, uint32_t time) { - _lastMessageTime = millis(); - - AsyncWebLockGuard l(_lock); - - if (!_controlQueue.empty()) { - auto & head = _controlQueue.front(); - if (head.finished()) { - len -= head.len(); - if (_status == WS_DISCONNECTING && head.opcode() == WS_DISCONNECT) { - _controlQueue.pop_front(); - _status = WS_DISCONNECTED; - l.unlock(); - if (_client) - _client->close(true); - return; - } - _controlQueue.pop_front(); - } - } - - if (len && !_messageQueue.empty()) { - _messageQueue.front().ack(len, time); - } - - _clearQueue(); - - _runQueue(); -} - -void AsyncWebSocketClient::_onPoll() { - if (!_client) - return; - - AsyncWebLockGuard l(_lock); - if (_client->canSend() && (!_controlQueue.empty() || !_messageQueue.empty())) { - l.unlock(); - _runQueue(); - } else if (_keepAlivePeriod > 0 && (millis() - _lastMessageTime) >= _keepAlivePeriod && (_controlQueue.empty() && _messageQueue.empty())) { - l.unlock(); - ping((uint8_t *)AWSC_PING_PAYLOAD, AWSC_PING_PAYLOAD_LEN); - } -} - -void AsyncWebSocketClient::_runQueue() { - if (!_client) - return; - - AsyncWebLockGuard l(_lock); - - _clearQueue(); - - if (!_controlQueue.empty() && (_messageQueue.empty() || _messageQueue.front().betweenFrames()) - && webSocketSendFrameWindow(_client) > (size_t)(_controlQueue.front().len() - 1)) { - //l.unlock(); - _controlQueue.front().send(_client); - } else if (!_messageQueue.empty() && _messageQueue.front().betweenFrames() && webSocketSendFrameWindow(_client)) { - //l.unlock(); - _messageQueue.front().send(_client); - } -} - -bool AsyncWebSocketClient::queueIsFull() const { - size_t size; - { - AsyncWebLockGuard l(_lock); - size = _messageQueue.size(); - } - return (size >= WS_MAX_QUEUED_MESSAGES) || (_status != WS_CONNECTED); -} - -size_t AsyncWebSocketClient::queueLen() const { - AsyncWebLockGuard l(_lock); - - return _messageQueue.size() + _controlQueue.size(); -} - -bool AsyncWebSocketClient::canSend() const { - size_t size; - { - AsyncWebLockGuard l(_lock); - size = _messageQueue.size(); - } - return size < WS_MAX_QUEUED_MESSAGES; -} - -void AsyncWebSocketClient::_queueControl(uint8_t opcode, const uint8_t * data, size_t len, bool mask) { - if (!_client) - return; - - { - AsyncWebLockGuard l(_lock); - _controlQueue.emplace_back(opcode, data, len, mask); - } - - if (_client && _client->canSend()) - _runQueue(); -} - -void AsyncWebSocketClient::_queueMessage(std::shared_ptr> buffer, uint8_t opcode, bool mask) { - if (_status != WS_CONNECTED) - return; - - if (!_client) - return; - - if (buffer->size() == 0) - return; - - { - AsyncWebLockGuard l(_lock); - if (_messageQueue.size() >= WS_MAX_QUEUED_MESSAGES) { - l.unlock(); - if (closeWhenFull) { -#ifdef ESP8266 - ets_printf("AsyncWebSocketClient::_queueMessage: Too many messages queued: closing connection\n"); -#else - log_e("Too many messages queued: closing connection"); -#endif - _status = WS_DISCONNECTED; - if (_client) - _client->close(true); - } else { -#ifdef ESP8266 - ets_printf("AsyncWebSocketClient::_queueMessage: Too many messages queued: discarding new message\n"); -#else - log_e("Too many messages queued: discarding new message"); -#endif - } - return; - } else { - _messageQueue.emplace_back(buffer, opcode, mask); - } - } - - if (_client && _client->canSend()) - _runQueue(); -} - -void AsyncWebSocketClient::close(uint16_t code, const char * message) { - if (_status != WS_CONNECTED) - return; - - if (code) { - uint8_t packetLen = 2; - if (message != NULL) { - size_t mlen = strlen(message); - if (mlen > 123) - mlen = 123; - packetLen += mlen; - } - char * buf = (char *)malloc(packetLen); - if (buf != NULL) { - buf[0] = (uint8_t)(code >> 8); - buf[1] = (uint8_t)(code & 0xFF); - if (message != NULL) { - memcpy(buf + 2, message, packetLen - 2); - } - _queueControl(WS_DISCONNECT, (uint8_t *)buf, packetLen); - free(buf); - return; - } - } - _queueControl(WS_DISCONNECT); -} - -void AsyncWebSocketClient::ping(const uint8_t * data, size_t len) { - if (_status == WS_CONNECTED) - _queueControl(WS_PING, data, len); -} - -void AsyncWebSocketClient::_onError(int8_t) { - //Serial.println("onErr"); -} - -void AsyncWebSocketClient::_onTimeout(uint32_t time) { - // Serial.println("onTime"); - (void)time; - _client->close(true); -} - -void AsyncWebSocketClient::_onDisconnect() { - // Serial.println("onDis"); - _client = NULL; -} - -void AsyncWebSocketClient::_onData(void * pbuf, size_t plen) { - // Serial.println("onData"); - _lastMessageTime = millis(); - uint8_t * data = (uint8_t *)pbuf; - while (plen > 0) { - if (!_pstate) { - const uint8_t * fdata = data; - _pinfo.index = 0; - _pinfo.final = (fdata[0] & 0x80) != 0; - _pinfo.opcode = fdata[0] & 0x0F; - _pinfo.masked = (fdata[1] & 0x80) != 0; - _pinfo.len = fdata[1] & 0x7F; - data += 2; - plen -= 2; - if (_pinfo.len == 126) { - _pinfo.len = fdata[3] | (uint16_t)(fdata[2]) << 8; - data += 2; - plen -= 2; - } else if (_pinfo.len == 127) { - _pinfo.len = fdata[9] | (uint16_t)(fdata[8]) << 8 | (uint32_t)(fdata[7]) << 16 | (uint32_t)(fdata[6]) << 24 | (uint64_t)(fdata[5]) << 32 - | (uint64_t)(fdata[4]) << 40 | (uint64_t)(fdata[3]) << 48 | (uint64_t)(fdata[2]) << 56; - data += 8; - plen -= 8; - } - - if (_pinfo.masked) { - memcpy(_pinfo.mask, data, 4); - data += 4; - plen -= 4; - } - } - - const size_t datalen = std::min((size_t)(_pinfo.len - _pinfo.index), plen); - const auto datalast = data[datalen]; - - if (_pinfo.masked) { - for (size_t i = 0; i < datalen; i++) - data[i] ^= _pinfo.mask[(_pinfo.index + i) % 4]; - } - - if ((datalen + _pinfo.index) < _pinfo.len) { - _pstate = 1; - - if (_pinfo.index == 0) { - if (_pinfo.opcode) { - _pinfo.message_opcode = _pinfo.opcode; - _pinfo.num = 0; - } - } - if (datalen > 0) - _server->_handleEvent(this, WS_EVT_DATA, (void *)&_pinfo, (uint8_t *)data, datalen); - - _pinfo.index += datalen; - } else if ((datalen + _pinfo.index) == _pinfo.len) { - _pstate = 0; - if (_pinfo.opcode == WS_DISCONNECT) { - if (datalen) { - uint16_t reasonCode = (uint16_t)(data[0] << 8) + data[1]; - char * reasonString = (char *)(data + 2); - if (reasonCode > 1001) { - _server->_handleEvent(this, WS_EVT_ERROR, (void *)&reasonCode, (uint8_t *)reasonString, strlen(reasonString)); - } - } - if (_status == WS_DISCONNECTING) { - _status = WS_DISCONNECTED; - _client->close(true); - } else { - _status = WS_DISCONNECTING; - _client->ackLater(); - _queueControl(WS_DISCONNECT, data, datalen); - } - } else if (_pinfo.opcode == WS_PING) { - _queueControl(WS_PONG, data, datalen); - } else if (_pinfo.opcode == WS_PONG) { - if (datalen != AWSC_PING_PAYLOAD_LEN || memcmp(AWSC_PING_PAYLOAD, data, AWSC_PING_PAYLOAD_LEN) != 0) - _server->_handleEvent(this, WS_EVT_PONG, NULL, data, datalen); - } else if (_pinfo.opcode < 8) { //continuation or text/binary frame - _server->_handleEvent(this, WS_EVT_DATA, (void *)&_pinfo, data, datalen); - if (_pinfo.final) - _pinfo.num = 0; - else - _pinfo.num += 1; - } - } else { - //os_printf("frame error: len: %u, index: %llu, total: %llu\n", datalen, _pinfo.index, _pinfo.len); - //what should we do? - break; - } - - // restore byte as _handleEvent may have added a null terminator i.e., data[len] = 0; - if (datalen > 0) - data[datalen] = datalast; - - data += datalen; - plen -= datalen; - } -} - -size_t AsyncWebSocketClient::printf(const char * format, ...) { - va_list arg; - va_start(arg, format); - char * temp = new char[MAX_PRINTF_LEN]; - if (!temp) { - va_end(arg); - return 0; - } - char * buffer = temp; - size_t len = vsnprintf(temp, MAX_PRINTF_LEN, format, arg); - va_end(arg); - - if (len > (MAX_PRINTF_LEN - 1)) { - buffer = new char[len + 1]; - if (!buffer) { - delete[] temp; - return 0; - } - va_start(arg, format); - vsnprintf(buffer, len + 1, format, arg); - va_end(arg); - } - text(buffer, len); - if (buffer != temp) { - delete[] buffer; - } - delete[] temp; - return len; -} - -#ifndef ESP32 -size_t AsyncWebSocketClient::printf_P(PGM_P formatP, ...) { - va_list arg; - va_start(arg, formatP); - char * temp = new char[MAX_PRINTF_LEN]; - if (!temp) { - va_end(arg); - return 0; - } - char * buffer = temp; - size_t len = vsnprintf_P(temp, MAX_PRINTF_LEN, formatP, arg); - va_end(arg); - - if (len > (MAX_PRINTF_LEN - 1)) { - buffer = new char[len + 1]; - if (!buffer) { - delete[] temp; - return 0; - } - va_start(arg, formatP); - vsnprintf_P(buffer, len + 1, formatP, arg); - va_end(arg); - } - text(buffer, len); - if (buffer != temp) { - delete[] buffer; - } - delete[] temp; - return len; -} -#endif - -namespace { -std::shared_ptr> makeSharedBuffer(const uint8_t * message, size_t len) { - auto buffer = std::make_shared>(len); - std::memcpy(buffer->data(), message, len); - return buffer; -} -} // namespace - -void AsyncWebSocketClient::text(AsyncWebSocketMessageBuffer * buffer) { - if (buffer) { - text(std::move(buffer->_buffer)); - delete buffer; - } -} - -void AsyncWebSocketClient::text(std::shared_ptr> buffer) { - _queueMessage(buffer); -} - -void AsyncWebSocketClient::text(const uint8_t * message, size_t len) { - text(makeSharedBuffer(message, len)); -} - -void AsyncWebSocketClient::text(const char * message, size_t len) { - text((const uint8_t *)message, len); -} - -void AsyncWebSocketClient::text(const char * message) { - text(message, strlen(message)); -} - -void AsyncWebSocketClient::text(const String & message) { - text(message.c_str(), message.length()); -} - -void AsyncWebSocketClient::text(const __FlashStringHelper * data) { - PGM_P p = reinterpret_cast(data); - - size_t n = 0; - while (1) { - if (pgm_read_byte(p + n) == 0) - break; - n += 1; - } - - char * message = (char *)malloc(n + 1); - if (message) { - memcpy_P(message, p, n); - message[n] = 0; - text(message, n); - free(message); - } -} - -void AsyncWebSocketClient::binary(AsyncWebSocketMessageBuffer * buffer) { - if (buffer) { - binary(std::move(buffer->_buffer)); - delete buffer; - } -} - -void AsyncWebSocketClient::binary(std::shared_ptr> buffer) { - _queueMessage(buffer, WS_BINARY); -} - -void AsyncWebSocketClient::binary(const uint8_t * message, size_t len) { - binary(makeSharedBuffer(message, len)); -} - -void AsyncWebSocketClient::binary(const char * message, size_t len) { - binary((const uint8_t *)message, len); -} - -void AsyncWebSocketClient::binary(const char * message) { - binary(message, strlen(message)); -} - -void AsyncWebSocketClient::binary(const String & message) { - binary(message.c_str(), message.length()); -} - -void AsyncWebSocketClient::binary(const __FlashStringHelper * data, size_t len) { - PGM_P p = reinterpret_cast(data); - char * message = (char *)malloc(len); - if (message) { - memcpy_P(message, p, len); - binary(message, len); - free(message); - } -} - -IPAddress AsyncWebSocketClient::remoteIP() const { - if (!_client) - return IPAddress((uint32_t)0U); - - return _client->remoteIP(); -} - -uint16_t AsyncWebSocketClient::remotePort() const { - if (!_client) - return 0; - - return _client->remotePort(); -} - - - -/* - * Async Web Socket - Each separate socket location - */ - -AsyncWebSocket::AsyncWebSocket(const String & url) - : _url(url) - , _cNextId(1) - , _enabled(true) { - _eventHandler = NULL; -} - -AsyncWebSocket::~AsyncWebSocket() { -} - -void AsyncWebSocket::_handleEvent(AsyncWebSocketClient * client, AwsEventType type, void * arg, uint8_t * data, size_t len) { - if (_eventHandler != NULL) { - _eventHandler(this, client, type, arg, data, len); - } -} - -AsyncWebSocketClient * AsyncWebSocket::_newClient(AsyncWebServerRequest * request) { - _clients.emplace_back(request, this); - return &_clients.back(); -} - -bool AsyncWebSocket::availableForWriteAll() { - return std::none_of(std::begin(_clients), std::end(_clients), [](const AsyncWebSocketClient & c) { return c.queueIsFull(); }); -} - -bool AsyncWebSocket::availableForWrite(uint32_t id) { - const auto iter = std::find_if(std::begin(_clients), std::end(_clients), [id](const AsyncWebSocketClient & c) { return c.id() == id; }); - if (iter == std::end(_clients)) - return true; - return !iter->queueIsFull(); -} - -size_t AsyncWebSocket::count() const { - return std::count_if(std::begin(_clients), std::end(_clients), [](const AsyncWebSocketClient & c) { return c.status() == WS_CONNECTED; }); -} - -AsyncWebSocketClient * AsyncWebSocket::client(uint32_t id) { - const auto iter = - std::find_if(std::begin(_clients), std::end(_clients), [id](const AsyncWebSocketClient & c) { return c.id() == id && c.status() == WS_CONNECTED; }); - if (iter == std::end(_clients)) - return nullptr; - - return &(*iter); -} - - -void AsyncWebSocket::close(uint32_t id, uint16_t code, const char * message) { - if (AsyncWebSocketClient * c = client(id)) - c->close(code, message); -} - -void AsyncWebSocket::closeAll(uint16_t code, const char * message) { - for (auto & c : _clients) - if (c.status() == WS_CONNECTED) - c.close(code, message); -} - -void AsyncWebSocket::cleanupClients(uint16_t maxClients) { - if (count() > maxClients) - _clients.front().close(); - - for (auto iter = std::begin(_clients); iter != std::end(_clients);) { - if (iter->shouldBeDeleted()) - iter = _clients.erase(iter); - else - iter++; - } -} - -void AsyncWebSocket::ping(uint32_t id, const uint8_t * data, size_t len) { - if (AsyncWebSocketClient * c = client(id)) - c->ping(data, len); -} - -void AsyncWebSocket::pingAll(const uint8_t * data, size_t len) { - for (auto & c : _clients) - if (c.status() == WS_CONNECTED) - c.ping(data, len); -} - -void AsyncWebSocket::text(uint32_t id, const uint8_t * message, size_t len) { - if (AsyncWebSocketClient * c = client(id)) - c->text(makeSharedBuffer(message, len)); -} -void AsyncWebSocket::text(uint32_t id, const char * message, size_t len) { - text(id, (const uint8_t *)message, len); -} -void AsyncWebSocket::text(uint32_t id, const char * message) { - text(id, message, strlen(message)); -} -void AsyncWebSocket::text(uint32_t id, const String & message) { - text(id, message.c_str(), message.length()); -} -void AsyncWebSocket::text(uint32_t id, const __FlashStringHelper * data) { - PGM_P p = reinterpret_cast(data); - - size_t n = 0; - while (true) { - if (pgm_read_byte(p + n) == 0) - break; - n += 1; - } - - char * message = (char *)malloc(n + 1); - if (message) { - memcpy_P(message, p, n); - message[n] = 0; - text(id, message, n); - free(message); - } -} -void AsyncWebSocket::text(uint32_t id, AsyncWebSocketMessageBuffer * buffer) { - if (buffer) { - text(id, std::move(buffer->_buffer)); - delete buffer; - } -} -void AsyncWebSocket::text(uint32_t id, std::shared_ptr> buffer) { - if (AsyncWebSocketClient * c = client(id)) - c->text(buffer); -} - -void AsyncWebSocket::textAll(const uint8_t * message, size_t len) { - textAll(makeSharedBuffer(message, len)); -} -void AsyncWebSocket::textAll(const char * message, size_t len) { - textAll((const uint8_t *)message, len); -} -void AsyncWebSocket::textAll(const char * message) { - textAll(message, strlen(message)); -} -void AsyncWebSocket::textAll(const String & message) { - textAll(message.c_str(), message.length()); -} -void AsyncWebSocket::textAll(const __FlashStringHelper * data) { - PGM_P p = reinterpret_cast(data); - - size_t n = 0; - while (1) { - if (pgm_read_byte(p + n) == 0) - break; - n += 1; - } - - char * message = (char *)malloc(n + 1); - if (message) { - memcpy_P(message, p, n); - message[n] = 0; - textAll(message, n); - free(message); - } -} -void AsyncWebSocket::textAll(AsyncWebSocketMessageBuffer * buffer) { - if (buffer) { - textAll(std::move(buffer->_buffer)); - delete buffer; - } -} - -void AsyncWebSocket::textAll(std::shared_ptr> buffer) { - for (auto & c : _clients) - if (c.status() == WS_CONNECTED) - c.text(buffer); -} - -void AsyncWebSocket::binary(uint32_t id, const uint8_t * message, size_t len) { - if (AsyncWebSocketClient * c = client(id)) - c->binary(makeSharedBuffer(message, len)); -} -void AsyncWebSocket::binary(uint32_t id, const char * message, size_t len) { - binary(id, (const uint8_t *)message, len); -} -void AsyncWebSocket::binary(uint32_t id, const char * message) { - binary(id, message, strlen(message)); -} -void AsyncWebSocket::binary(uint32_t id, const String & message) { - binary(id, message.c_str(), message.length()); -} -void AsyncWebSocket::binary(uint32_t id, const __FlashStringHelper * data, size_t len) { - PGM_P p = reinterpret_cast(data); - char * message = (char *)malloc(len); - if (message) { - memcpy_P(message, p, len); - binary(id, message, len); - free(message); - } -} -void AsyncWebSocket::binary(uint32_t id, AsyncWebSocketMessageBuffer * buffer) { - if (buffer) { - binary(id, std::move(buffer->_buffer)); - delete buffer; - } -} -void AsyncWebSocket::binary(uint32_t id, std::shared_ptr> buffer) { - if (AsyncWebSocketClient * c = client(id)) - c->binary(buffer); -} - - -void AsyncWebSocket::binaryAll(const uint8_t * message, size_t len) { - binaryAll(makeSharedBuffer(message, len)); -} -void AsyncWebSocket::binaryAll(const char * message, size_t len) { - binaryAll((const uint8_t *)message, len); -} -void AsyncWebSocket::binaryAll(const char * message) { - binaryAll(message, strlen(message)); -} -void AsyncWebSocket::binaryAll(const String & message) { - binaryAll(message.c_str(), message.length()); -} -void AsyncWebSocket::binaryAll(const __FlashStringHelper * data, size_t len) { - PGM_P p = reinterpret_cast(data); - char * message = (char *)malloc(len); - if (message) { - memcpy_P(message, p, len); - binaryAll(message, len); - free(message); - } -} -void AsyncWebSocket::binaryAll(AsyncWebSocketMessageBuffer * buffer) { - if (buffer) { - binaryAll(std::move(buffer->_buffer)); - delete buffer; - } -} -void AsyncWebSocket::binaryAll(std::shared_ptr> buffer) { - for (auto & c : _clients) - if (c.status() == WS_CONNECTED) - c.binary(buffer); -} - -size_t AsyncWebSocket::printf(uint32_t id, const char * format, ...) { - AsyncWebSocketClient * c = client(id); - if (c) { - va_list arg; - va_start(arg, format); - size_t len = c->printf(format, arg); - va_end(arg); - return len; - } - return 0; -} - -size_t AsyncWebSocket::printfAll(const char * format, ...) { - va_list arg; - char * temp = new char[MAX_PRINTF_LEN]; - if (!temp) - return 0; - - va_start(arg, format); - size_t len = vsnprintf(temp, MAX_PRINTF_LEN, format, arg); - va_end(arg); - delete[] temp; - - std::shared_ptr> buffer = std::make_shared>(len); - - va_start(arg, format); - vsnprintf((char *)buffer->data(), len + 1, format, arg); - va_end(arg); - - textAll(buffer); - return len; -} - -#ifndef ESP32 -size_t AsyncWebSocket::printf_P(uint32_t id, PGM_P formatP, ...) { - AsyncWebSocketClient * c = client(id); - if (c != NULL) { - va_list arg; - va_start(arg, formatP); - size_t len = c->printf_P(formatP, arg); - va_end(arg); - return len; - } - return 0; -} -#endif - -size_t AsyncWebSocket::printfAll_P(PGM_P formatP, ...) { - va_list arg; - char * temp = new char[MAX_PRINTF_LEN]; - if (!temp) - return 0; - - va_start(arg, formatP); - size_t len = vsnprintf_P(temp, MAX_PRINTF_LEN, formatP, arg); - va_end(arg); - delete[] temp; - - std::shared_ptr> buffer = std::make_shared>(len + 1); - - va_start(arg, formatP); - vsnprintf_P((char *)buffer->data(), len + 1, formatP, arg); - va_end(arg); - - textAll(buffer); - return len; -} - -const char __WS_STR_CONNECTION[] PROGMEM = {"Connection"}; -const char __WS_STR_UPGRADE[] PROGMEM = {"Upgrade"}; -const char __WS_STR_ORIGIN[] PROGMEM = {"Origin"}; -const char __WS_STR_COOKIE[] PROGMEM = {"Cookie"}; -const char __WS_STR_VERSION[] PROGMEM = {"Sec-WebSocket-Version"}; -const char __WS_STR_KEY[] PROGMEM = {"Sec-WebSocket-Key"}; -const char __WS_STR_PROTOCOL[] PROGMEM = {"Sec-WebSocket-Protocol"}; -const char __WS_STR_ACCEPT[] PROGMEM = {"Sec-WebSocket-Accept"}; -const char __WS_STR_UUID[] PROGMEM = {"258EAFA5-E914-47DA-95CA-C5AB0DC85B11"}; - -#define WS_STR_CONNECTION FPSTR(__WS_STR_CONNECTION) -#define WS_STR_UPGRADE FPSTR(__WS_STR_UPGRADE) -#define WS_STR_ORIGIN FPSTR(__WS_STR_ORIGIN) -#define WS_STR_COOKIE FPSTR(__WS_STR_COOKIE) -#define WS_STR_VERSION FPSTR(__WS_STR_VERSION) -#define WS_STR_KEY FPSTR(__WS_STR_KEY) -#define WS_STR_PROTOCOL FPSTR(__WS_STR_PROTOCOL) -#define WS_STR_ACCEPT FPSTR(__WS_STR_ACCEPT) -#define WS_STR_UUID FPSTR(__WS_STR_UUID) - -bool AsyncWebSocket::canHandle(AsyncWebServerRequest * request) { - if (!_enabled) - return false; - - if (request->method() != HTTP_GET || !request->url().equals(_url) || !request->isExpectedRequestedConnType(RCT_WS)) - return false; - - request->addInterestingHeader(WS_STR_CONNECTION); - request->addInterestingHeader(WS_STR_UPGRADE); - request->addInterestingHeader(WS_STR_ORIGIN); - request->addInterestingHeader(WS_STR_COOKIE); - request->addInterestingHeader(WS_STR_VERSION); - request->addInterestingHeader(WS_STR_KEY); - request->addInterestingHeader(WS_STR_PROTOCOL); - return true; -} - -void AsyncWebSocket::handleRequest(AsyncWebServerRequest * request) { - if (!request->hasHeader(WS_STR_VERSION) || !request->hasHeader(WS_STR_KEY)) { - request->send(400); - return; - } - if ((_username.length() && _password.length()) && !request->authenticate(_username.c_str(), _password.c_str())) { - return request->requestAuthentication(); - } - if (_handshakeHandler != nullptr) { - if (!_handshakeHandler(request)) { - request->send(401); - return; - } - } - AsyncWebHeader * version = request->getHeader(WS_STR_VERSION); - if (version->value().toInt() != 13) { - AsyncWebServerResponse * response = request->beginResponse(400); - response->addHeader(WS_STR_VERSION, F("13")); - request->send(response); - return; - } - AsyncWebHeader * key = request->getHeader(WS_STR_KEY); - AsyncWebServerResponse * response = new AsyncWebSocketResponse(key->value(), this); - if (request->hasHeader(WS_STR_PROTOCOL)) { - AsyncWebHeader * protocol = request->getHeader(WS_STR_PROTOCOL); - //ToDo: check protocol - response->addHeader(WS_STR_PROTOCOL, protocol->value()); - } - request->send(response); -} - -AsyncWebSocketMessageBuffer * AsyncWebSocket::makeBuffer(size_t size) { - AsyncWebSocketMessageBuffer * buffer = new AsyncWebSocketMessageBuffer(size); - if (buffer->length() != size) { - delete buffer; - return nullptr; - } else { - return buffer; - } -} - -AsyncWebSocketMessageBuffer * AsyncWebSocket::makeBuffer(uint8_t * data, size_t size) { - AsyncWebSocketMessageBuffer * buffer = new AsyncWebSocketMessageBuffer(data, size); - if (buffer->length() != size) { - delete buffer; - return nullptr; - } else { - return buffer; - } -} - -/* - * Response to Web Socket request - sends the authorization and detaches the TCP Client from the web server - * Authentication code from https://github.com/Links2004/arduinoWebSockets/blob/master/src/WebSockets.cpp#L480 - */ - -AsyncWebSocketResponse::AsyncWebSocketResponse(const String & key, AsyncWebSocket * server) { - _server = server; - _code = 101; - _sendContentLength = false; - - uint8_t * hash = (uint8_t *)malloc(20); - if (hash == NULL) { - _state = RESPONSE_FAILED; - return; - } - char * buffer = (char *)malloc(33); - if (buffer == NULL) { - free(hash); - _state = RESPONSE_FAILED; - return; - } -#ifdef ESP8266 - sha1(key + WS_STR_UUID, hash); -#else - String k = key + WS_STR_UUID; - SHA1Builder sha1; - sha1.begin(); - sha1.add((const uint8_t *)k.c_str(), k.length()); - sha1.calculate(); - sha1.getBytes(hash); -#endif - base64_encodestate _state; - base64_init_encodestate(&_state); - int len = base64_encode_block((const char *)hash, 20, buffer, &_state); - len = base64_encode_blockend((buffer + len), &_state); - addHeader(WS_STR_CONNECTION, WS_STR_UPGRADE); - addHeader(WS_STR_UPGRADE, F("websocket")); - addHeader(WS_STR_ACCEPT, buffer); - free(buffer); - free(hash); -} - -void AsyncWebSocketResponse::_respond(AsyncWebServerRequest * request) { - if (_state == RESPONSE_FAILED) { - request->client()->close(true); - return; - } - String out = _assembleHead(request->version()); - request->client()->write(out.c_str(), _headLength); - _state = RESPONSE_WAIT_ACK; -} - -size_t AsyncWebSocketResponse::_ack(AsyncWebServerRequest * request, size_t len, uint32_t time) { - (void)time; - - if (len) - _server->_newClient(request); - - return 0; -} \ No newline at end of file diff --git a/lib/ESPAsyncWebServer/src/AsyncWebSocket.h b/lib/ESPAsyncWebServer/src/AsyncWebSocket.h deleted file mode 100644 index b744a08bd..000000000 --- a/lib/ESPAsyncWebServer/src/AsyncWebSocket.h +++ /dev/null @@ -1,398 +0,0 @@ -/* - Asynchronous WebServer library for Espressif MCUs - - Copyright (c) 2016 Hristo Gochkov. All rights reserved. - This file is part of the esp8266 core 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 -*/ -#ifndef ASYNCWEBSOCKET_H_ -#define ASYNCWEBSOCKET_H_ - -#include -#ifdef ESP32 -#include -#ifndef WS_MAX_QUEUED_MESSAGES -#define WS_MAX_QUEUED_MESSAGES 32 -#endif -#else -#include -#ifndef WS_MAX_QUEUED_MESSAGES -#define WS_MAX_QUEUED_MESSAGES 8 -#endif -#endif -#include - -#include "AsyncWebSynchronization.h" - -#include -#include -#include - -#ifdef ESP8266 -#include -#ifdef CRYPTO_HASH_h // include Hash.h from espressif framework if the first include was from the crypto library -#include <../src/Hash.h> -#endif -#endif - -#ifdef ESP32 -#define DEFAULT_MAX_WS_CLIENTS 8 -#else -#define DEFAULT_MAX_WS_CLIENTS 4 -#endif - -class AsyncWebSocket; -class AsyncWebSocketResponse; -class AsyncWebSocketClient; -class AsyncWebSocketControl; - -typedef struct { - /** Message type as defined by enum AwsFrameType. - * Note: Applications will only see WS_TEXT and WS_BINARY. - * All other types are handled by the library. */ - uint8_t message_opcode; - /** Frame number of a fragmented message. */ - uint32_t num; - /** Is this the last frame in a fragmented message ?*/ - uint8_t final; - /** Is this frame masked? */ - uint8_t masked; - /** Message type as defined by enum AwsFrameType. - * This value is the same as message_opcode for non-fragmented - * messages, but may also be WS_CONTINUATION in a fragmented message. */ - uint8_t opcode; - /** Length of the current frame. - * This equals the total length of the message if num == 0 && final == true */ - uint64_t len; - /** Mask key */ - uint8_t mask[4]; - /** Offset of the data inside the current frame. */ - uint64_t index; -} AwsFrameInfo; - -typedef enum { WS_DISCONNECTED, WS_CONNECTED, WS_DISCONNECTING } AwsClientStatus; -typedef enum { WS_CONTINUATION, WS_TEXT, WS_BINARY, WS_DISCONNECT = 0x08, WS_PING, WS_PONG } AwsFrameType; -typedef enum { WS_MSG_SENDING, WS_MSG_SENT, WS_MSG_ERROR } AwsMessageStatus; -typedef enum { WS_EVT_CONNECT, WS_EVT_DISCONNECT, WS_EVT_PONG, WS_EVT_ERROR, WS_EVT_DATA } AwsEventType; - -class AsyncWebSocketMessageBuffer { - friend AsyncWebSocket; - friend AsyncWebSocketClient; - - private: - std::shared_ptr> _buffer; - - public: - AsyncWebSocketMessageBuffer(); - AsyncWebSocketMessageBuffer(size_t size); - AsyncWebSocketMessageBuffer(uint8_t * data, size_t size); - ~AsyncWebSocketMessageBuffer(); - bool reserve(size_t size); - uint8_t * get() { - return _buffer->data(); - } - size_t length() const { - return _buffer->size(); - } -}; - -class AsyncWebSocketMessage { - private: - std::shared_ptr> _WSbuffer; - uint8_t _opcode{WS_TEXT}; - bool _mask{false}; - AwsMessageStatus _status{WS_MSG_ERROR}; - size_t _sent{}; - size_t _ack{}; - size_t _acked{}; - - public: - AsyncWebSocketMessage(std::shared_ptr> buffer, uint8_t opcode = WS_TEXT, bool mask = false); - - bool finished() const { - return _status != WS_MSG_SENDING; - } - bool betweenFrames() const { - return _acked == _ack; - } - - void ack(size_t len, uint32_t time); - size_t send(AsyncClient * client); -}; - -class AsyncWebSocketClient { - private: - AsyncClient * _client; - AsyncWebSocket * _server; - uint32_t _clientId; - AwsClientStatus _status; - - AsyncWebLock _lock; - - std::deque _controlQueue; - std::deque _messageQueue; - bool closeWhenFull = true; - - uint8_t _pstate; - AwsFrameInfo _pinfo; - - uint32_t _lastMessageTime; - uint32_t _keepAlivePeriod; - - void _queueControl(uint8_t opcode, const uint8_t * data = NULL, size_t len = 0, bool mask = false); - void _queueMessage(std::shared_ptr> buffer, uint8_t opcode = WS_TEXT, bool mask = false); - void _runQueue(); - void _clearQueue(); - - public: - void * _tempObject; - - AsyncWebSocketClient(AsyncWebServerRequest * request, AsyncWebSocket * server); - ~AsyncWebSocketClient(); - - //client id increments for the given server - uint32_t id() const { - return _clientId; - } - AwsClientStatus status() const { - return _status; - } - AsyncClient * client() { - return _client; - } - const AsyncClient * client() const { - return _client; - } - AsyncWebSocket * server() { - return _server; - } - const AsyncWebSocket * server() const { - return _server; - } - AwsFrameInfo const & pinfo() const { - return _pinfo; - } - - // - If "true" (default), the connection will be closed if the message queue is full. - // This is the default behavior in yubox-node-org, which is not silently discarding messages but instead closes the connection. - // The big issue with this behavior is that is can cause the UI to automatically re-create a new WS connection, which can be filled again, - // and so on, causing a resource exhaustion. - // - // - If "false", the incoming message will be discarded if the queue is full. - // This is the default behavior in the original ESPAsyncWebServer library from me-no-dev. - // This behavior allows the best performance at the expense of unreliable message delivery in case the queue is full (some messages may be lost). - // - // - In any case, when the queue is full, a message is logged. - // - IT is recommended to use the methods queueIsFull(), availableForWriteAll(), availableForWrite(clientId) to check if the queue is full before sending a message. - // - // Usage: - // - can be set in the onEvent listener when connecting (event type is: WS_EVT_CONNECT) - // - // Use cases:, - // - if using websocket to send logging messages, maybe some loss is acceptable. - // - But if using websocket to send UI update messages, maybe the connection should be closed and the UI redrawn. - void setCloseClientOnQueueFull(bool close) { - closeWhenFull = close; - } - bool willCloseClientOnQueueFull() const { - return closeWhenFull; - } - - IPAddress remoteIP() const; - uint16_t remotePort() const; - - bool shouldBeDeleted() const { - return !_client; - } - - //control frames - void close(uint16_t code = 0, const char * message = NULL); - void ping(const uint8_t * data = NULL, size_t len = 0); - - //set auto-ping period in seconds. disabled if zero (default) - void keepAlivePeriod(uint16_t seconds) { - _keepAlivePeriod = seconds * 1000; - } - uint16_t keepAlivePeriod() { - return (uint16_t)(_keepAlivePeriod / 1000); - } - - //data packets - void message(std::shared_ptr> buffer, uint8_t opcode = WS_TEXT, bool mask = false) { - _queueMessage(buffer, opcode, mask); - } - bool queueIsFull() const; - size_t queueLen() const; - - size_t printf(const char * format, ...) __attribute__((format(printf, 2, 3))); -#ifndef ESP32 - size_t printf_P(PGM_P formatP, ...) __attribute__((format(printf, 2, 3))); -#endif - - void text(std::shared_ptr> buffer); - void text(const uint8_t * message, size_t len); - void text(const char * message, size_t len); - void text(const char * message); - void text(const String & message); - void text(const __FlashStringHelper * message); - void text(AsyncWebSocketMessageBuffer * buffer); - - void binary(std::shared_ptr> buffer); - void binary(const uint8_t * message, size_t len); - void binary(const char * message, size_t len); - void binary(const char * message); - void binary(const String & message); - void binary(const __FlashStringHelper * message, size_t len); - void binary(AsyncWebSocketMessageBuffer * buffer); - - bool canSend() const; - - //system callbacks (do not call) - void _onAck(size_t len, uint32_t time); - void _onError(int8_t); - void _onPoll(); - void _onTimeout(uint32_t time); - void _onDisconnect(); - void _onData(void * pbuf, size_t plen); -}; - -typedef std::function AwsHandshakeHandler; -typedef std::function AwsEventHandler; - -//WebServer Handler implementation that plays the role of a socket server -class AsyncWebSocket : public AsyncWebHandler { - private: - String _url; - std::list _clients; - uint32_t _cNextId; - AwsEventHandler _eventHandler; - AwsHandshakeHandler _handshakeHandler; - bool _enabled; - AsyncWebLock _lock; - - public: - AsyncWebSocket(const String & url); - ~AsyncWebSocket(); - const char * url() const { - return _url.c_str(); - } - void enable(bool e) { - _enabled = e; - } - bool enabled() const { - return _enabled; - } - bool availableForWriteAll(); - bool availableForWrite(uint32_t id); - - size_t count() const; - AsyncWebSocketClient * client(uint32_t id); - bool hasClient(uint32_t id) { - return client(id) != NULL; - } - - void close(uint32_t id, uint16_t code = 0, const char * message = NULL); - void closeAll(uint16_t code = 0, const char * message = NULL); - void cleanupClients(uint16_t maxClients = DEFAULT_MAX_WS_CLIENTS); - - void ping(uint32_t id, const uint8_t * data = NULL, size_t len = 0); - void pingAll(const uint8_t * data = NULL, size_t len = 0); // done - - void text(uint32_t id, const uint8_t * message, size_t len); - void text(uint32_t id, const char * message, size_t len); - void text(uint32_t id, const char * message); - void text(uint32_t id, const String & message); - void text(uint32_t id, const __FlashStringHelper * message); - void text(uint32_t id, AsyncWebSocketMessageBuffer * buffer); - void text(uint32_t id, std::shared_ptr> buffer); - - void textAll(const uint8_t * message, size_t len); - void textAll(const char * message, size_t len); - void textAll(const char * message); - void textAll(const String & message); - void textAll(const __FlashStringHelper * message); - void textAll(AsyncWebSocketMessageBuffer * buffer); - void textAll(std::shared_ptr> buffer); - - void binary(uint32_t id, const uint8_t * message, size_t len); - void binary(uint32_t id, const char * message, size_t len); - void binary(uint32_t id, const char * message); - void binary(uint32_t id, const String & message); - void binary(uint32_t id, const __FlashStringHelper * message, size_t len); - void binary(uint32_t id, AsyncWebSocketMessageBuffer * buffer); - void binary(uint32_t id, std::shared_ptr> buffer); - - void binaryAll(const uint8_t * message, size_t len); - void binaryAll(const char * message, size_t len); - void binaryAll(const char * message); - void binaryAll(const String & message); - void binaryAll(const __FlashStringHelper * message, size_t len); - void binaryAll(AsyncWebSocketMessageBuffer * buffer); - void binaryAll(std::shared_ptr> buffer); - - size_t printf(uint32_t id, const char * format, ...) __attribute__((format(printf, 3, 4))); - size_t printfAll(const char * format, ...) __attribute__((format(printf, 2, 3))); -#ifndef ESP32 - size_t printf_P(uint32_t id, PGM_P formatP, ...) __attribute__((format(printf, 3, 4))); -#endif - size_t printfAll_P(PGM_P formatP, ...) __attribute__((format(printf, 2, 3))); - - //event listener - void onEvent(AwsEventHandler handler) { - _eventHandler = handler; - } - - // Handshake Handler - void handleHandshake(AwsHandshakeHandler handler) { - _handshakeHandler = handler; - } - - //system callbacks (do not call) - uint32_t _getNextId() { - return _cNextId++; - } - AsyncWebSocketClient * _newClient(AsyncWebServerRequest * request); - void _handleEvent(AsyncWebSocketClient * client, AwsEventType type, void * arg, uint8_t * data, size_t len); - virtual bool canHandle(AsyncWebServerRequest * request) override final; - virtual void handleRequest(AsyncWebServerRequest * request) override final; - - - // messagebuffer functions/objects. - AsyncWebSocketMessageBuffer * makeBuffer(size_t size = 0); - AsyncWebSocketMessageBuffer * makeBuffer(uint8_t * data, size_t size); - - const std::list & getClients() const { - return _clients; - } -}; - -//WebServer response to authenticate the socket and detach the tcp client from the web server request -class AsyncWebSocketResponse : public AsyncWebServerResponse { - private: - String _content; - AsyncWebSocket * _server; - - public: - AsyncWebSocketResponse(const String & key, AsyncWebSocket * server); - void _respond(AsyncWebServerRequest * request); - size_t _ack(AsyncWebServerRequest * request, size_t len, uint32_t time); - bool _sourceValid() const { - return true; - } -}; - - -#endif /* ASYNCWEBSOCKET_H_ */ \ No newline at end of file diff --git a/lib/ESPAsyncWebServer/src/AsyncWebSynchronization.h b/lib/ESPAsyncWebServer/src/AsyncWebSynchronization.h deleted file mode 100644 index 0ff8ab63b..000000000 --- a/lib/ESPAsyncWebServer/src/AsyncWebSynchronization.h +++ /dev/null @@ -1,134 +0,0 @@ -#ifndef ASYNCWEBSYNCHRONIZATION_H_ -#define ASYNCWEBSYNCHRONIZATION_H_ - -// Synchronisation is only available on ESP32, as the ESP8266 isn't using FreeRTOS by default - -#include - -#ifdef ESP32 - -// This is the ESP32 version of the Sync Lock, using the FreeRTOS Semaphore -// Modified 'AsyncWebLock' to just only use mutex since pxCurrentTCB is not -// always available. According to example by Arjan Filius, changed name, -// added unimplemented version for ESP8266 -class AsyncPlainLock -{ -private: - SemaphoreHandle_t _lock; - -public: - AsyncPlainLock() { - _lock = xSemaphoreCreateBinary(); - // In this fails, the system is likely that much out of memory that - // we should abort anyways. If assertions are disabled, nothing is lost.. - assert(_lock); - xSemaphoreGive(_lock); - } - - ~AsyncPlainLock() { - vSemaphoreDelete(_lock); - } - - bool lock() const { - xSemaphoreTake(_lock, portMAX_DELAY); - return true; - } - - void unlock() const { - xSemaphoreGive(_lock); - } -}; - -// This is the ESP32 version of the Sync Lock, using the FreeRTOS Semaphore -class AsyncWebLock -{ -private: - SemaphoreHandle_t _lock; - mutable TaskHandle_t _lockedBy{}; - -public: - AsyncWebLock() - { - _lock = xSemaphoreCreateBinary(); - // In this fails, the system is likely that much out of memory that - // we should abort anyways. If assertions are disabled, nothing is lost.. - assert(_lock); - _lockedBy = NULL; - xSemaphoreGive(_lock); - } - - ~AsyncWebLock() { - vSemaphoreDelete(_lock); - } - - bool lock() const { - const auto currentTask = xTaskGetCurrentTaskHandle(); - if (_lockedBy != currentTask) { - xSemaphoreTake(_lock, portMAX_DELAY); - _lockedBy = currentTask; - return true; - } - return false; - } - - void unlock() const { - _lockedBy = NULL; - xSemaphoreGive(_lock); - } -}; - -#else - -// This is the 8266 version of the Sync Lock which is currently unimplemented -class AsyncWebLock -{ - -public: - AsyncWebLock() { - } - - ~AsyncWebLock() { - } - - bool lock() const { - return false; - } - - void unlock() const { - } -}; - -// Same for AsyncPlainLock, for ESP8266 this is just the unimplemented version above. -using AsyncPlainLock = AsyncWebLock; - -#endif - -class AsyncWebLockGuard -{ -private: - const AsyncWebLock *_lock; - -public: - AsyncWebLockGuard(const AsyncWebLock &l) { - if (l.lock()) { - _lock = &l; - } else { - _lock = NULL; - } - } - - ~AsyncWebLockGuard() { - if (_lock) { - _lock->unlock(); - } - } - - void unlock() { - if (_lock) { - _lock->unlock(); - _lock = NULL; - } - } -}; - -#endif // ASYNCWEBSYNCHRONIZATION_H_ diff --git a/lib/ESPAsyncWebServer/src/ESPAsyncWebServer.h b/lib/ESPAsyncWebServer/src/ESPAsyncWebServer.h deleted file mode 100644 index c864d40ce..000000000 --- a/lib/ESPAsyncWebServer/src/ESPAsyncWebServer.h +++ /dev/null @@ -1,616 +0,0 @@ -/* - Asynchronous WebServer library for Espressif MCUs - - Copyright (c) 2016 Hristo Gochkov. All rights reserved. - This file is part of the esp8266 core 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 -*/ -#ifndef _ESPAsyncWebServer_H_ -#define _ESPAsyncWebServer_H_ - -#include "Arduino.h" - -#include -#include -#include -#include "FS.h" - -#include // added by proddy for EMS-ESP - -#include "StringArray.h" - -#ifdef ESP32 -#include -#include -#elif defined(ESP8266) -#include -#include -#else -#error Platform not supported -#endif - -#define ASYNCWEBSERVER_VERSION "2.6.1" -#define ASYNCWEBSERVER_VERSION_MAJOR 2 -#define ASYNCWEBSERVER_VERSION_MINOR 6 -#define ASYNCWEBSERVER_VERSION_REVISION 1 -#define ASYNCWEBSERVER_FORK_mathieucarbou - -#ifdef ASYNCWEBSERVER_REGEX -#define ASYNCWEBSERVER_REGEX_ATTRIBUTE -#else -#define ASYNCWEBSERVER_REGEX_ATTRIBUTE __attribute__((warning("ASYNCWEBSERVER_REGEX not defined"))) -#endif - -class AsyncWebServer; -class AsyncWebServerRequest; -class AsyncWebServerResponse; -class AsyncWebHeader; -class AsyncWebParameter; -class AsyncWebRewrite; -class AsyncWebHandler; -class AsyncStaticWebHandler; -class AsyncCallbackWebHandler; -class AsyncResponseStream; - -#ifndef WEBSERVER_H -typedef enum { - HTTP_GET = 0b00000001, - HTTP_POST = 0b00000010, - HTTP_DELETE = 0b00000100, - HTTP_PUT = 0b00001000, - HTTP_PATCH = 0b00010000, - HTTP_HEAD = 0b00100000, - HTTP_OPTIONS = 0b01000000, - HTTP_ANY = 0b01111111, -} WebRequestMethod; -#endif - -#ifndef HAVE_FS_FILE_OPEN_MODE -namespace fs { -class FileOpenMode { - public: - static const char * read; - static const char * write; - static const char * append; -}; -}; // namespace fs -#else -#include "FileOpenMode.h" -#endif - -//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 - -typedef uint8_t WebRequestMethodComposite; -typedef std::function ArDisconnectHandler; - -/* - * PARAMETER :: Chainable object to hold GET/POST and FILE parameters - * */ - -class AsyncWebParameter { - private: - String _name; - String _value; - size_t _size; - bool _isForm; - bool _isFile; - - public: - AsyncWebParameter(const String & name, const String & value, bool form = false, bool file = false, size_t size = 0) - : _name(name) - , _value(value) - , _size(size) - , _isForm(form) - , _isFile(file) { - } - const String & name() const { - return _name; - } - const String & value() const { - return _value; - } - size_t size() const { - return _size; - } - bool isPost() const { - return _isForm; - } - bool isFile() const { - return _isFile; - } -}; - -/* - * HEADER :: Chainable object to hold the headers - * */ - -class AsyncWebHeader { - private: - String _name; - String _value; - - public: - AsyncWebHeader() = default; - AsyncWebHeader(const AsyncWebHeader &) = default; - - AsyncWebHeader(const String & name, const String & value) - : _name(name) - , _value(value) { - } - AsyncWebHeader(const String & data) - : _name() - , _value() { - if (!data) - return; - int index = data.indexOf(':'); - if (index < 0) - return; - _name = data.substring(0, index); - _value = data.substring(index + 2); - } - - AsyncWebHeader & operator=(const AsyncWebHeader &) = default; - - const String & name() const { - return _name; - } - const String & value() const { - return _value; - } - String toString() const { - return _name + F(": ") + _value + F("\r\n"); - } -}; - -/* - * REQUEST :: Each incoming Client is wrapped inside a Request and both live together until disconnect - * */ - -typedef enum { RCT_NOT_USED = -1, RCT_DEFAULT = 0, RCT_HTTP, RCT_WS, RCT_EVENT, RCT_MAX } RequestedConnectionType; - -typedef std::function AwsResponseFiller; -typedef std::function AwsTemplateProcessor; - -class AsyncWebServerRequest { - using File = fs::File; - using FS = fs::FS; - friend class AsyncWebServer; - friend class AsyncCallbackWebHandler; - - private: - AsyncClient * _client; - AsyncWebServer * _server; - AsyncWebHandler * _handler; - AsyncWebServerResponse * _response; - std::vector _interestingHeaders; - ArDisconnectHandler _onDisconnectfn; - - String _temp; - uint8_t _parseState; - - uint8_t _version; - WebRequestMethodComposite _method; - String _url; - String _host; - String _contentType; - String _boundary; - String _authorization; - RequestedConnectionType _reqconntype; - void _removeNotInterestingHeaders(); - bool _isDigest; - bool _isMultipart; - bool _isPlainPost; - bool _expectingContinue; - size_t _contentLength; - size_t _parsedLength; - - std::list _headers; - LinkedList _params; - std::vector _pathParams; - - uint8_t _multiParseState; - uint8_t _boundaryPosition; - size_t _itemStartIndex; - size_t _itemSize; - String _itemName; - String _itemFilename; - String _itemType; - String _itemValue; - uint8_t * _itemBuffer; - size_t _itemBufferIndex; - bool _itemIsFile; - - void _onPoll(); - void _onAck(size_t len, uint32_t time); - void _onError(int8_t error); - void _onTimeout(uint32_t time); - void _onDisconnect(); - void _onData(void * buf, size_t len); - - void _addParam(AsyncWebParameter *); - void _addPathParam(const char * param); - - bool _parseReqHead(); - bool _parseReqHeader(); - void _parseLine(); - void _parsePlainPostChar(uint8_t data); - void _parseMultipartPostByte(uint8_t data, bool last); - void _addGetParams(const String & params); - - void _handleUploadStart(); - void _handleUploadByte(uint8_t data, bool last); - void _handleUploadEnd(); - - public: - File _tempFile; - void * _tempObject; - - AsyncWebServerRequest(AsyncWebServer *, AsyncClient *); - ~AsyncWebServerRequest(); - - AsyncClient * client() { - return _client; - } - uint8_t version() const { - return _version; - } - WebRequestMethodComposite method() const { - return _method; - } - const String & url() const { - return _url; - } - const String & host() const { - return _host; - } - const String & contentType() const { - return _contentType; - } - size_t contentLength() const { - return _contentLength; - } - bool multipart() const { - return _isMultipart; - } - const __FlashStringHelper * methodToString() const; - const __FlashStringHelper * requestedConnTypeToString() const; - RequestedConnectionType requestedConnType() const { - return _reqconntype; - } - bool isExpectedRequestedConnType(RequestedConnectionType erct1, RequestedConnectionType erct2 = RCT_NOT_USED, RequestedConnectionType erct3 = RCT_NOT_USED); - void onDisconnect(ArDisconnectHandler fn); - - //hash is the string representation of: - // base64(user:pass) for basic or - // user:realm:md5(user:realm:pass) for digest - bool authenticate(const char * hash); - bool authenticate(const char * username, const char * password, const char * realm = NULL, bool passwordIsHash = false); - void requestAuthentication(const char * realm = NULL, bool isDigest = true); - - void setHandler(AsyncWebHandler * handler) { - _handler = handler; - } - void addInterestingHeader(const String & name); - - void redirect(const String & url); - - void send(AsyncWebServerResponse * response); - void send(int code, const String & contentType = String(), const String & content = String()); - void send(FS & fs, const String & path, const String & contentType = String(), bool download = false, AwsTemplateProcessor callback = nullptr); - void send(File content, const String & path, const String & contentType = String(), bool download = false, AwsTemplateProcessor callback = nullptr); - void send(Stream & stream, const String & contentType, size_t len, AwsTemplateProcessor callback = nullptr); - void send(const String & contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback = nullptr); - void sendChunked(const String & contentType, AwsResponseFiller callback, AwsTemplateProcessor templateCallback = nullptr); - void send_P(int code, const String & contentType, const uint8_t * content, size_t len, AwsTemplateProcessor callback = nullptr); - void send_P(int code, const String & contentType, PGM_P content, AwsTemplateProcessor callback = nullptr); - - AsyncWebServerResponse * beginResponse(int code, const String & contentType = String(), const String & content = String()); - AsyncWebServerResponse * - beginResponse(FS & fs, const String & path, const String & contentType = String(), bool download = false, AwsTemplateProcessor callback = nullptr); - AsyncWebServerResponse * - beginResponse(File content, const String & path, const String & contentType = String(), bool download = false, AwsTemplateProcessor callback = nullptr); - AsyncWebServerResponse * beginResponse(Stream & stream, const String & contentType, size_t len, AwsTemplateProcessor callback = nullptr); - AsyncWebServerResponse * beginResponse(const String & contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback = nullptr); - AsyncWebServerResponse * beginChunkedResponse(const String & contentType, AwsResponseFiller callback, AwsTemplateProcessor templateCallback = nullptr); - AsyncResponseStream * beginResponseStream(const String & contentType, size_t bufferSize = 1460); - AsyncWebServerResponse * beginResponse_P(int code, const String & contentType, const uint8_t * content, size_t len, AwsTemplateProcessor callback = nullptr); - AsyncWebServerResponse * beginResponse_P(int code, const String & contentType, PGM_P content, AwsTemplateProcessor callback = nullptr); - AsyncWebServerResponse * beginResponse(const String & contentType, const uint8_t * content, size_t len); // added by proddy for EMS-ESP - - size_t headers() const; // get header count - bool hasHeader(const String & name) const; // check if header exists - bool hasHeader(const __FlashStringHelper * data) const; // check if header exists - - AsyncWebHeader * getHeader(const String & name); - const AsyncWebHeader * getHeader(const String & name) const; - AsyncWebHeader * getHeader(const __FlashStringHelper * data); - const AsyncWebHeader * getHeader(const __FlashStringHelper * data) const; - AsyncWebHeader * getHeader(size_t num); - const AsyncWebHeader * getHeader(size_t num) const; - - size_t params() const; // get arguments count - bool hasParam(const String & name, bool post = false, bool file = false) const; - bool hasParam(const __FlashStringHelper * data, bool post = false, bool file = false) const; - - AsyncWebParameter * getParam(const String & name, bool post = false, bool file = false) const; - AsyncWebParameter * getParam(const __FlashStringHelper * data, bool post, bool file) const; - AsyncWebParameter * getParam(size_t num) const; - - size_t args() const { - return params(); - } // get arguments count - const String & arg(const String & name) const; // get request argument value by name - const String & arg(const __FlashStringHelper * data) const; // get request argument value by F(name) - const String & arg(size_t i) const; // get request argument value by number - const String & argName(size_t i) const; // get request argument name by number - bool hasArg(const char * name) const; // check if argument exists - bool hasArg(const __FlashStringHelper * data) const; // check if F(argument) exists - - const String & ASYNCWEBSERVER_REGEX_ATTRIBUTE pathArg(size_t i) const; - - const String & header(const char * name) const; // get request header value by name - const String & header(const __FlashStringHelper * data) const; // get request header value by F(name) - const String & header(size_t i) const; // get request header value by number - const String & headerName(size_t i) const; // get request header name by number - String urlDecode(const String & text) const; -}; - -/* - * FILTER :: Callback to filter AsyncWebRewrite and AsyncWebHandler (done by the Server) - * */ - -typedef std::function ArRequestFilterFunction; - -bool ON_STA_FILTER(AsyncWebServerRequest * request); - -bool ON_AP_FILTER(AsyncWebServerRequest * request); - -/* - * REWRITE :: One instance can be handle any Request (done by the Server) - * */ - -class AsyncWebRewrite { - protected: - String _from; - String _toUrl; - String _params; - ArRequestFilterFunction _filter; - - public: - AsyncWebRewrite(const char * from, const char * to) - : _from(from) - , _toUrl(to) - , _params(String()) - , _filter(NULL) { - int index = _toUrl.indexOf('?'); - if (index > 0) { - _params = _toUrl.substring(index + 1); - _toUrl = _toUrl.substring(0, index); - } - } - virtual ~AsyncWebRewrite() { - } - AsyncWebRewrite & setFilter(ArRequestFilterFunction fn) { - _filter = fn; - return *this; - } - bool filter(AsyncWebServerRequest * request) const { - return _filter == NULL || _filter(request); - } - const String & from(void) const { - return _from; - } - const String & toUrl(void) const { - return _toUrl; - } - const String & params(void) const { - return _params; - } - virtual bool match(AsyncWebServerRequest * request) { - return from() == request->url() && filter(request); - } -}; - -/* - * HANDLER :: One instance can be attached to any Request (done by the Server) - * */ - -class AsyncWebHandler { - protected: - ArRequestFilterFunction _filter; - String _username; - String _password; - - public: - AsyncWebHandler() - : _username("") - , _password("") { - } - AsyncWebHandler & setFilter(ArRequestFilterFunction fn) { - _filter = fn; - return *this; - } - AsyncWebHandler & setAuthentication(const String & username, const String & password) { - _username = username; - _password = password; - return *this; - }; - AsyncWebHandler & setAuthentication(const char * username, const char * password) { - _username = String(username); - _password = String(password); - return *this; - }; - bool filter(AsyncWebServerRequest * request) { - return _filter == NULL || _filter(request); - } - virtual ~AsyncWebHandler() { - } - virtual bool canHandle(AsyncWebServerRequest * request __attribute__((unused))) { - return false; - } - virtual void handleRequest(AsyncWebServerRequest * request __attribute__((unused))) { - } - virtual void handleUpload(AsyncWebServerRequest * request __attribute__((unused)), - const String & filename __attribute__((unused)), - size_t index __attribute__((unused)), - uint8_t * data __attribute__((unused)), - size_t len __attribute__((unused)), - bool final __attribute__((unused))) { - } - virtual void handleBody(AsyncWebServerRequest * request __attribute__((unused)), - uint8_t * data __attribute__((unused)), - size_t len __attribute__((unused)), - size_t index __attribute__((unused)), - size_t total __attribute__((unused))) { - } - virtual bool isRequestHandlerTrivial() { - return true; - } -}; - -/* - * RESPONSE :: One instance is created for each Request (attached by the Handler) - * */ - -typedef enum { RESPONSE_SETUP, RESPONSE_HEADERS, RESPONSE_CONTENT, RESPONSE_WAIT_ACK, RESPONSE_END, RESPONSE_FAILED } WebResponseState; - -class AsyncWebServerResponse { - protected: - int _code; - std::list _headers; - String _contentType; - size_t _contentLength; - bool _sendContentLength; - bool _chunked; - size_t _headLength; - size_t _sentLength; - size_t _ackedLength; - size_t _writtenLength; - WebResponseState _state; - const char * _responseCodeToString(int code); - - public: - static const __FlashStringHelper * responseCodeToString(int code); - - public: - AsyncWebServerResponse(); - virtual ~AsyncWebServerResponse(); - virtual void setCode(int code); - virtual void setContentLength(size_t len); - virtual void setContentType(const String & type); - virtual void addHeader(const String & name, const String & value); - virtual String _assembleHead(uint8_t version); - virtual bool _started() const; - virtual bool _finished() const; - virtual bool _failed() const; - virtual bool _sourceValid() const; - virtual void _respond(AsyncWebServerRequest * request); - virtual size_t _ack(AsyncWebServerRequest * request, size_t len, uint32_t time); -}; - -/* - * SERVER :: One instance - * */ - -typedef std::function ArRequestHandlerFunction; -typedef std::function ArUploadHandlerFunction; -typedef std::function ArBodyHandlerFunction; - -typedef std::function ArJsonRequestHandlerFunction; // added by proddy for EMS-ESP - -class AsyncWebServer { - protected: - AsyncServer _server; - LinkedList _rewrites; - LinkedList _handlers; - AsyncCallbackWebHandler * _catchAllHandler; - - public: - AsyncWebServer(uint16_t port); - ~AsyncWebServer(); - - void begin(); - void end(); - -#if ASYNC_TCP_SSL_ENABLED - void onSslFileRequest(AcSSlFileHandler cb, void * arg); - void beginSecure(const char * cert, const char * private_key_file, const char * password); -#endif - - AsyncWebRewrite & addRewrite(AsyncWebRewrite * rewrite); - bool removeRewrite(AsyncWebRewrite * rewrite); - AsyncWebRewrite & rewrite(const char * from, const char * to); - - AsyncWebHandler & addHandler(AsyncWebHandler * handler); - bool removeHandler(AsyncWebHandler * handler); - - AsyncCallbackWebHandler & on(const char * uri, ArRequestHandlerFunction onRequest); - AsyncCallbackWebHandler & on(const char * uri, WebRequestMethodComposite method, ArRequestHandlerFunction onRequest); - AsyncCallbackWebHandler & on(const char * uri, WebRequestMethodComposite method, ArRequestHandlerFunction onRequest, ArUploadHandlerFunction onUpload); - AsyncCallbackWebHandler & - on(const char * uri, WebRequestMethodComposite method, ArRequestHandlerFunction onRequest, ArUploadHandlerFunction onUpload, ArBodyHandlerFunction onBody); - - void on(const char * uri, ArJsonRequestHandlerFunction onRequest); // added by proddy for EMS-ESP - - AsyncStaticWebHandler & serveStatic(const char * uri, fs::FS & fs, const char * path, const char * cache_control = NULL); - - void onNotFound(ArRequestHandlerFunction fn); //called when handler is not assigned - void onFileUpload(ArUploadHandlerFunction fn); //handle file uploads - void onRequestBody(ArBodyHandlerFunction fn); //handle posts with plain body content (JSON often transmitted this way as a request) - - void reset(); //remove all writers and handlers, with onNotFound/onFileUpload/onRequestBody - - void _handleDisconnect(AsyncWebServerRequest * request); - void _attachHandler(AsyncWebServerRequest * request); - void _rewriteRequest(AsyncWebServerRequest * request); -}; - -class DefaultHeaders { - using headers_t = std::list; - headers_t _headers; - - public: - DefaultHeaders() = default; - - using ConstIterator = headers_t::const_iterator; - - void addHeader(const String & name, const String & value) { - _headers.emplace_back(name, value); - } - - ConstIterator begin() const { - return _headers.begin(); - } - ConstIterator end() const { - return _headers.end(); - } - - DefaultHeaders(DefaultHeaders const &) = delete; - DefaultHeaders & operator=(DefaultHeaders const &) = delete; - - static DefaultHeaders & Instance() { - static DefaultHeaders instance; - return instance; - } -}; - -#include "WebResponseImpl.h" -#include "WebHandlerImpl.h" -#include "AsyncWebSocket.h" -#include "AsyncEventSource.h" - -#endif /* _AsyncWebServer_H_ */ diff --git a/lib/ESPAsyncWebServer/src/ESP_Async_WebServer.h b/lib/ESPAsyncWebServer/src/ESP_Async_WebServer.h deleted file mode 100644 index ca6a11262..000000000 --- a/lib/ESPAsyncWebServer/src/ESP_Async_WebServer.h +++ /dev/null @@ -1,2 +0,0 @@ -// to please Arduino Lint -#include "ESPAsyncWebServer.h" diff --git a/lib/ESPAsyncWebServer/src/StringArray.h b/lib/ESPAsyncWebServer/src/StringArray.h deleted file mode 100644 index d5096e699..000000000 --- a/lib/ESPAsyncWebServer/src/StringArray.h +++ /dev/null @@ -1,174 +0,0 @@ -/* - Asynchronous WebServer library for Espressif MCUs - - Copyright (c) 2016 Hristo Gochkov. All rights reserved. - This file is part of the esp8266 core 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 -*/ -#ifndef STRINGARRAY_H_ -#define STRINGARRAY_H_ - -#include "stddef.h" -#include "WString.h" - -template -class LinkedListNode { - T _value; - public: - LinkedListNode* next; - LinkedListNode(const T val): _value(val), next(nullptr) {} - ~LinkedListNode(){} - const T& value() const { return _value; }; - T& value(){ return _value; } -}; - -template class Item = LinkedListNode> -class LinkedList { - public: - typedef Item ItemType; - typedef std::function OnRemove; - typedef std::function Predicate; - private: - ItemType* _root; - OnRemove _onRemove; - - class Iterator { - ItemType* _node; - public: - Iterator(ItemType* current = nullptr) : _node(current) {} - Iterator(const Iterator& i) : _node(i._node) {} - Iterator& operator ++() { _node = _node->next; return *this; } - bool operator != (const Iterator& i) const { return _node != i._node; } - const T& operator * () const { return _node->value(); } - const T* operator -> () const { return &_node->value(); } - }; - - public: - typedef const Iterator ConstIterator; - ConstIterator begin() const { return ConstIterator(_root); } - ConstIterator end() const { return ConstIterator(nullptr); } - - LinkedList(OnRemove onRemove) : _root(nullptr), _onRemove(onRemove) {} - ~LinkedList(){} - void add(const T& t){ - auto it = new ItemType(t); - if(!_root){ - _root = it; - } else { - auto i = _root; - while(i->next) i = i->next; - i->next = it; - } - } - T& front() const { - return _root->value(); - } - - bool isEmpty() const { - return _root == nullptr; - } - size_t length() const { - size_t i = 0; - auto it = _root; - while(it){ - i++; - it = it->next; - } - return i; - } - size_t count_if(Predicate predicate) const { - size_t i = 0; - auto it = _root; - while(it){ - if (!predicate){ - i++; - } - else if (predicate(it->value())) { - i++; - } - it = it->next; - } - return i; - } - const T* nth(size_t N) const { - size_t i = 0; - auto it = _root; - while(it){ - if(i++ == N) - return &(it->value()); - it = it->next; - } - return nullptr; - } - bool remove(const T& t){ - auto it = _root; - auto pit = _root; - while(it){ - if(it->value() == t){ - if(it == _root){ - _root = _root->next; - } else { - pit->next = it->next; - } - - if (_onRemove) { - _onRemove(it->value()); - } - - delete it; - return true; - } - pit = it; - it = it->next; - } - return false; - } - bool remove_first(Predicate predicate){ - auto it = _root; - auto pit = _root; - while(it){ - if(predicate(it->value())){ - if(it == _root){ - _root = _root->next; - } else { - pit->next = it->next; - } - if (_onRemove) { - _onRemove(it->value()); - } - delete it; - return true; - } - pit = it; - it = it->next; - } - return false; - } - - void free(){ - while(_root != nullptr){ - auto it = _root; - _root = _root->next; - if (_onRemove) { - _onRemove(it->value()); - } - delete it; - } - _root = nullptr; - } -}; - -#endif /* STRINGARRAY_H_ */ diff --git a/lib/ESPAsyncWebServer/src/WebAuthentication.cpp b/lib/ESPAsyncWebServer/src/WebAuthentication.cpp deleted file mode 100644 index 656adbc61..000000000 --- a/lib/ESPAsyncWebServer/src/WebAuthentication.cpp +++ /dev/null @@ -1,249 +0,0 @@ -/* - Asynchronous WebServer library for Espressif MCUs - - Copyright (c) 2016 Hristo Gochkov. All rights reserved. - This file is part of the esp8266 core 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 -*/ -#include "WebAuthentication.h" -#include -#ifdef ESP32 -#include -#else -#include "md5.h" -#endif - - -// Basic Auth hash = base64("username:password") - -bool checkBasicAuthentication(const char * hash, const char * username, const char * password) { - if (username == NULL || password == NULL || hash == NULL) - return false; - - size_t toencodeLen = strlen(username) + strlen(password) + 1; - size_t encodedLen = base64_encode_expected_len(toencodeLen); - if (strlen(hash) != encodedLen) -// Fix from https://github.com/me-no-dev/ESPAsyncWebServer/issues/667 -#ifdef ARDUINO_ARCH_ESP32 - if (strlen(hash) != encodedLen) -#else - if (strlen(hash) != encodedLen - 1) -#endif - return false; - - char * toencode = new char[toencodeLen + 1]; - if (toencode == NULL) { - return false; - } - char * encoded = new char[base64_encode_expected_len(toencodeLen) + 1]; - if (encoded == NULL) { - delete[] toencode; - return false; - } - sprintf_P(toencode, PSTR("%s:%s"), username, password); - if (base64_encode_chars(toencode, toencodeLen, encoded) > 0 && memcmp(hash, encoded, encodedLen) == 0) { - delete[] toencode; - delete[] encoded; - return true; - } - delete[] toencode; - delete[] encoded; - return false; -} - -static bool getMD5(uint8_t * data, uint16_t len, char * output) { //33 bytes or more -#ifdef ESP32 - MD5Builder md5; - md5.begin(); - md5.add(data, len); - md5.calculate(); - md5.getChars(output); -#else - md5_context_t _ctx; - - uint8_t * _buf = (uint8_t *)malloc(16); - if (_buf == NULL) - return false; - memset(_buf, 0x00, 16); - - MD5Init(&_ctx); - MD5Update(&_ctx, data, len); - MD5Final(_buf, &_ctx); - - for (uint8_t i = 0; i < 16; i++) { - sprintf_P(output + (i * 2), PSTR("%02x"), _buf[i]); - } - - free(_buf); -#endif - return true; -} - -static String genRandomMD5() { -#ifdef ESP8266 - uint32_t r = RANDOM_REG32; -#else - uint32_t r = rand(); -#endif - char * out = (char *)malloc(33); - if (out == NULL || !getMD5((uint8_t *)(&r), 4, out)) - return emptyString; - String res = String(out); - free(out); - return res; -} - -static String stringMD5(const String & in) { - char * out = (char *)malloc(33); - if (out == NULL || !getMD5((uint8_t *)(in.c_str()), in.length(), out)) - return emptyString; - String res = String(out); - free(out); - return res; -} - -String generateDigestHash(const char * username, const char * password, const char * realm) { - if (username == NULL || password == NULL || realm == NULL) { - return emptyString; - } - char * out = (char *)malloc(33); - String res = String(username); - res += ':'; - res.concat(realm); - res += ':'; - String in = res; - in.concat(password); - if (out == NULL || !getMD5((uint8_t *)(in.c_str()), in.length(), out)) - return emptyString; - res.concat(out); - free(out); - return res; -} - -String requestDigestAuthentication(const char * realm) { - String header = F("realm=\""); - if (realm == NULL) - header.concat(F("asyncesp")); - else - header.concat(realm); - header.concat(F("\", qop=\"auth\", nonce=\"")); - header.concat(genRandomMD5()); - header.concat(F("\", opaque=\"")); - header.concat(genRandomMD5()); - header += '"'; - return header; -} - -bool checkDigestAuthentication(const char * header, - const __FlashStringHelper * method, - const char * username, - const char * password, - const char * realm, - bool passwordIsHash, - const char * nonce, - const char * opaque, - const char * uri) { - if (username == NULL || password == NULL || header == NULL || method == NULL) { - //os_printf("AUTH FAIL: missing requred fields\n"); - return false; - } - - String myHeader = String(header); - int nextBreak = myHeader.indexOf(','); - if (nextBreak < 0) { - //os_printf("AUTH FAIL: no variables\n"); - return false; - } - - String myUsername = String(); - String myRealm = String(); - String myNonce = String(); - String myUri = String(); - String myResponse = String(); - String myQop = String(); - String myNc = String(); - String myCnonce = String(); - - myHeader += F(", "); - do { - String avLine = myHeader.substring(0, nextBreak); - avLine.trim(); - myHeader = myHeader.substring(nextBreak + 1); - nextBreak = myHeader.indexOf(','); - - int eqSign = avLine.indexOf('='); - if (eqSign < 0) { - //os_printf("AUTH FAIL: no = sign\n"); - return false; - } - String varName = avLine.substring(0, eqSign); - avLine = avLine.substring(eqSign + 1); - if (avLine.startsWith(String('"'))) { - avLine = avLine.substring(1, avLine.length() - 1); - } - - if (varName.equals(F("username"))) { - if (!avLine.equals(username)) { - //os_printf("AUTH FAIL: username\n"); - return false; - } - myUsername = avLine; - } else if (varName.equals(F("realm"))) { - if (realm != NULL && !avLine.equals(realm)) { - //os_printf("AUTH FAIL: realm\n"); - return false; - } - myRealm = avLine; - } else if (varName.equals(F("nonce"))) { - if (nonce != NULL && !avLine.equals(nonce)) { - //os_printf("AUTH FAIL: nonce\n"); - return false; - } - myNonce = avLine; - } else if (varName.equals(F("opaque"))) { - if (opaque != NULL && !avLine.equals(opaque)) { - //os_printf("AUTH FAIL: opaque\n"); - return false; - } - } else if (varName.equals(F("uri"))) { - if (uri != NULL && !avLine.equals(uri)) { - //os_printf("AUTH FAIL: uri\n"); - return false; - } - myUri = avLine; - } else if (varName.equals(F("response"))) { - myResponse = avLine; - } else if (varName.equals(F("qop"))) { - myQop = avLine; - } else if (varName.equals(F("nc"))) { - myNc = avLine; - } else if (varName.equals(F("cnonce"))) { - myCnonce = avLine; - } - } while (nextBreak > 0); - - String ha1 = (passwordIsHash) ? String(password) : stringMD5(myUsername + ':' + myRealm + ':' + String(password)); - String ha2 = String(method) + ':' + myUri; - String response = ha1 + ':' + myNonce + ':' + myNc + ':' + myCnonce + ':' + myQop + ':' + stringMD5(ha2); - - if (myResponse.equals(stringMD5(response))) { - //os_printf("AUTH SUCCESS\n"); - return true; - } - - //os_printf("AUTH FAIL: password\n"); - return false; -} \ No newline at end of file diff --git a/lib/ESPAsyncWebServer/src/WebAuthentication.h b/lib/ESPAsyncWebServer/src/WebAuthentication.h deleted file mode 100644 index a6f1966e3..000000000 --- a/lib/ESPAsyncWebServer/src/WebAuthentication.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - Asynchronous WebServer library for Espressif MCUs - - Copyright (c) 2016 Hristo Gochkov. All rights reserved. - This file is part of the esp8266 core 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 -*/ - -#ifndef WEB_AUTHENTICATION_H_ -#define WEB_AUTHENTICATION_H_ - -#include "Arduino.h" - -bool checkBasicAuthentication(const char * header, const char * username, const char * password); -String requestDigestAuthentication(const char * realm); -bool checkDigestAuthentication(const char * header, const __FlashStringHelper *method, const char * username, const char * password, const char * realm, bool passwordIsHash, const char * nonce, const char * opaque, const char * uri); - -//for storing hashed versions on the device that can be authenticated against -String generateDigestHash(const char * username, const char * password, const char * realm); - -#endif diff --git a/lib/ESPAsyncWebServer/src/WebHandlerImpl.h b/lib/ESPAsyncWebServer/src/WebHandlerImpl.h deleted file mode 100644 index 9b7ba1b04..000000000 --- a/lib/ESPAsyncWebServer/src/WebHandlerImpl.h +++ /dev/null @@ -1,151 +0,0 @@ -/* - Asynchronous WebServer library for Espressif MCUs - - Copyright (c) 2016 Hristo Gochkov. All rights reserved. - This file is part of the esp8266 core 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 -*/ -#ifndef ASYNCWEBSERVERHANDLERIMPL_H_ -#define ASYNCWEBSERVERHANDLERIMPL_H_ - -#include -#ifdef ASYNCWEBSERVER_REGEX -#include -#endif - -#include "stddef.h" -#include - -class AsyncStaticWebHandler: public AsyncWebHandler { - using File = fs::File; - using FS = fs::FS; - private: - bool _getFile(AsyncWebServerRequest *request); - bool _fileExists(AsyncWebServerRequest *request, const String& path); - uint8_t _countBits(const uint8_t value) const; - protected: - FS _fs; - String _uri; - String _path; - String _default_file; - String _cache_control; - String _last_modified; - AwsTemplateProcessor _callback; - bool _isDir; - bool _gzipFirst; - uint8_t _gzipStats; - public: - AsyncStaticWebHandler(const char* uri, FS& fs, const char* path, const char* cache_control); - virtual bool canHandle(AsyncWebServerRequest *request) override final; - virtual void handleRequest(AsyncWebServerRequest *request) override final; - AsyncStaticWebHandler& setIsDir(bool isDir); - AsyncStaticWebHandler& setDefaultFile(const char* filename); - AsyncStaticWebHandler& setCacheControl(const char* cache_control); - AsyncStaticWebHandler& setLastModified(const char* last_modified); - AsyncStaticWebHandler& setLastModified(struct tm* last_modified); - #ifdef ESP8266 - AsyncStaticWebHandler& setLastModified(time_t last_modified); - AsyncStaticWebHandler& setLastModified(); //sets to current time. Make sure sntp is runing and time is updated - #endif - AsyncStaticWebHandler& setTemplateProcessor(AwsTemplateProcessor newCallback) {_callback = newCallback; return *this;} -}; - -class AsyncCallbackWebHandler: public AsyncWebHandler { - private: - protected: - String _uri; - WebRequestMethodComposite _method; - ArRequestHandlerFunction _onRequest; - ArUploadHandlerFunction _onUpload; - ArBodyHandlerFunction _onBody; - bool _isRegex; - public: - AsyncCallbackWebHandler() : _uri(), _method(HTTP_ANY), _onRequest(NULL), _onUpload(NULL), _onBody(NULL), _isRegex(false) {} - void setUri(const String& uri){ - _uri = uri; - _isRegex = uri.startsWith("^") && uri.endsWith("$"); - } - void setMethod(WebRequestMethodComposite method){ _method = method; } - void onRequest(ArRequestHandlerFunction fn){ _onRequest = fn; } - void onUpload(ArUploadHandlerFunction fn){ _onUpload = fn; } - void onBody(ArBodyHandlerFunction fn){ _onBody = fn; } - - virtual bool canHandle(AsyncWebServerRequest *request) override final{ - - if(!_onRequest) - return false; - - if(!(_method & request->method())) - return false; - -#ifdef ASYNCWEBSERVER_REGEX - if (_isRegex) { - std::regex pattern(_uri.c_str()); - std::smatch matches; - std::string s(request->url().c_str()); - if(std::regex_search(s, matches, pattern)) { - for (size_t i = 1; i < matches.size(); ++i) { // start from 1 - request->_addPathParam(matches[i].str().c_str()); - } - } else { - return false; - } - } else -#endif - if (_uri.length() && _uri.startsWith("/*.")) { - String uriTemplate = String (_uri); - uriTemplate = uriTemplate.substring(uriTemplate.lastIndexOf(".")); - if (!request->url().endsWith(uriTemplate)) - return false; - } - else - if (_uri.length() && _uri.endsWith("*")) { - String uriTemplate = String(_uri); - uriTemplate = uriTemplate.substring(0, uriTemplate.length() - 1); - if (!request->url().startsWith(uriTemplate)) - return false; - } - else if(_uri.length() && (_uri != request->url() && !request->url().startsWith(_uri+"/"))) - return false; - - request->addInterestingHeader("ANY"); - return true; - } - - virtual void handleRequest(AsyncWebServerRequest *request) override final { - if((_username != "" && _password != "") && !request->authenticate(_username.c_str(), _password.c_str())) - return request->requestAuthentication(); - if(_onRequest) - _onRequest(request); - else - request->send(500); - } - virtual void handleUpload(AsyncWebServerRequest *request, const String& filename, size_t index, uint8_t *data, size_t len, bool final) override final { - if((_username != "" && _password != "") && !request->authenticate(_username.c_str(), _password.c_str())) - return request->requestAuthentication(); - if(_onUpload) - _onUpload(request, filename, index, data, len, final); - } - virtual void handleBody(AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total) override final { - if((_username != "" && _password != "") && !request->authenticate(_username.c_str(), _password.c_str())) - return request->requestAuthentication(); - if(_onBody) - _onBody(request, data, len, index, total); - } - virtual bool isRequestHandlerTrivial() override final {return _onRequest ? false : true;} -}; - -#endif /* ASYNCWEBSERVERHANDLERIMPL_H_ */ diff --git a/lib/ESPAsyncWebServer/src/WebHandlers.cpp b/lib/ESPAsyncWebServer/src/WebHandlers.cpp deleted file mode 100644 index 0b55b0ddf..000000000 --- a/lib/ESPAsyncWebServer/src/WebHandlers.cpp +++ /dev/null @@ -1,233 +0,0 @@ -/* - Asynchronous WebServer library for Espressif MCUs - - Copyright (c) 2016 Hristo Gochkov. All rights reserved. - This file is part of the esp8266 core 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 -*/ -#include "ESPAsyncWebServer.h" -#include "WebHandlerImpl.h" - -AsyncStaticWebHandler::AsyncStaticWebHandler(const char* uri, FS& fs, const char* path, const char* cache_control) - : _fs(fs), _uri(uri), _path(path), _default_file(F("index.htm")), _cache_control(cache_control), _last_modified(), _callback(nullptr) -{ - // Ensure leading '/' - if (_uri.length() == 0 || _uri[0] != '/') _uri = String('/') + _uri; - if (_path.length() == 0 || _path[0] != '/') _path = String('/') + _path; - - // If path ends with '/' we assume a hint that this is a directory to improve performance. - // However - if it does not end with '/' we, can't assume a file, path can still be a directory. - _isDir = _path[_path.length()-1] == '/'; - - // Remove the trailing '/' so we can handle default file - // Notice that root will be "" not "/" - if (_uri[_uri.length()-1] == '/') _uri = _uri.substring(0, _uri.length()-1); - if (_path[_path.length()-1] == '/') _path = _path.substring(0, _path.length()-1); - - // Reset stats - _gzipFirst = false; - _gzipStats = 0xF8; -} - -AsyncStaticWebHandler& AsyncStaticWebHandler::setIsDir(bool isDir){ - _isDir = isDir; - return *this; -} - -AsyncStaticWebHandler& AsyncStaticWebHandler::setDefaultFile(const char* filename){ - _default_file = String(filename); - return *this; -} - -AsyncStaticWebHandler& AsyncStaticWebHandler::setCacheControl(const char* cache_control){ - _cache_control = String(cache_control); - return *this; -} - -AsyncStaticWebHandler& AsyncStaticWebHandler::setLastModified(const char* last_modified){ - _last_modified = last_modified; - return *this; -} - -AsyncStaticWebHandler& AsyncStaticWebHandler::setLastModified(struct tm* last_modified){ - auto formatP = PSTR("%a, %d %b %Y %H:%M:%S %Z"); - char format[strlen_P(formatP) + 1]; - strcpy_P(format, formatP); - - char result[30]; - strftime(result, sizeof(result), format, last_modified); - return setLastModified((const char *)result); -} - -#ifdef ESP8266 -AsyncStaticWebHandler& AsyncStaticWebHandler::setLastModified(time_t last_modified){ - return setLastModified((struct tm *)gmtime(&last_modified)); -} - -AsyncStaticWebHandler& AsyncStaticWebHandler::setLastModified(){ - time_t last_modified; - if(time(&last_modified) == 0) //time is not yet set - return *this; - return setLastModified(last_modified); -} -#endif -bool AsyncStaticWebHandler::canHandle(AsyncWebServerRequest *request){ - if(request->method() != HTTP_GET - || !request->url().startsWith(_uri) - || !request->isExpectedRequestedConnType(RCT_DEFAULT, RCT_HTTP) - ){ - return false; - } - if (_getFile(request)) { - // We interested in "If-Modified-Since" header to check if file was modified - if (_last_modified.length()) - request->addInterestingHeader(F("If-Modified-Since")); - - if(_cache_control.length()) - request->addInterestingHeader(F("If-None-Match")); - - return true; - } - - return false; -} - -bool AsyncStaticWebHandler::_getFile(AsyncWebServerRequest *request) -{ - // Remove the found uri - String path = request->url().substring(_uri.length()); - - // We can skip the file check and look for default if request is to the root of a directory or that request path ends with '/' - bool canSkipFileCheck = (_isDir && path.length() == 0) || (path.length() && path[path.length()-1] == '/'); - - path = _path + path; - - // Do we have a file or .gz file - if (!canSkipFileCheck && _fileExists(request, path)) - return true; - - // Can't handle if not default file - if (_default_file.length() == 0) - return false; - - // Try to add default file, ensure there is a trailing '/' ot the path. - if (path.length() == 0 || path[path.length()-1] != '/') - path += String('/'); - path += _default_file; - - return _fileExists(request, path); -} - -#ifdef ESP32 -#define FILE_IS_REAL(f) (f == true && !f.isDirectory()) -#else -#define FILE_IS_REAL(f) (f == true) -#endif - -bool AsyncStaticWebHandler::_fileExists(AsyncWebServerRequest *request, const String& path) -{ - bool fileFound = false; - bool gzipFound = false; - - String gzip = path + F(".gz"); - - if (_gzipFirst) { - if (_fs.exists(gzip)) { - request->_tempFile = _fs.open(gzip, fs::FileOpenMode::read); - gzipFound = FILE_IS_REAL(request->_tempFile); - } - if (!gzipFound){ - if (_fs.exists(path)) { - request->_tempFile = _fs.open(path, fs::FileOpenMode::read); - fileFound = FILE_IS_REAL(request->_tempFile); - } - } - } else { - if (_fs.exists(path)) { - request->_tempFile = _fs.open(path, fs::FileOpenMode::read); - fileFound = FILE_IS_REAL(request->_tempFile); - } - if (!fileFound){ - if (_fs.exists(gzip)) { - request->_tempFile = _fs.open(gzip, fs::FileOpenMode::read); - gzipFound = FILE_IS_REAL(request->_tempFile); - } - } - } - - bool found = fileFound || gzipFound; - - if (found) { - // Extract the file name from the path and keep it in _tempObject - size_t pathLen = path.length(); - char * _tempPath = (char*)malloc(pathLen+1); - snprintf_P(_tempPath, pathLen+1, PSTR("%s"), path.c_str()); - request->_tempObject = (void*)_tempPath; - - // Calculate gzip statistic - _gzipStats = (_gzipStats << 1) + (gzipFound ? 1 : 0); - if (_gzipStats == 0x00) _gzipFirst = false; // All files are not gzip - else if (_gzipStats == 0xFF) _gzipFirst = true; // All files are gzip - else _gzipFirst = _countBits(_gzipStats) > 4; // IF we have more gzip files - try gzip first - } - - return found; -} - -uint8_t AsyncStaticWebHandler::_countBits(const uint8_t value) const -{ - uint8_t w = value; - uint8_t n; - for (n=0; w!=0; n++) w&=w-1; - return n; -} - -void AsyncStaticWebHandler::handleRequest(AsyncWebServerRequest *request) -{ - // Get the filename from request->_tempObject and free it - String filename = String((char*)request->_tempObject); - free(request->_tempObject); - request->_tempObject = NULL; - if((_username.length() && _password.length()) && !request->authenticate(_username.c_str(), _password.c_str())) - return request->requestAuthentication(); - - if (request->_tempFile == true) { - time_t lw = request->_tempFile.getLastWrite(); // get last file mod time (if supported by FS) - if (lw) setLastModified(gmtime(&lw)); - String etag(lw ? lw : request->_tempFile.size()); // set etag to lastmod timestamp if available, otherwise to size - if (_last_modified.length() && _last_modified == request->header(F("If-Modified-Since"))) { - request->_tempFile.close(); - request->send(304); // Not modified - } else if (_cache_control.length() && request->hasHeader(F("If-None-Match")) && request->header(F("If-None-Match")).equals(etag)) { - request->_tempFile.close(); - AsyncWebServerResponse * response = new AsyncBasicResponse(304); // Not modified - response->addHeader(F("Cache-Control"), _cache_control); - response->addHeader(F("ETag"), etag); - request->send(response); - } else { - AsyncWebServerResponse * response = new AsyncFileResponse(request->_tempFile, filename, String(), false, _callback); - if (_last_modified.length()) - response->addHeader(F("Last-Modified"), _last_modified); - if (_cache_control.length()){ - response->addHeader(F("Cache-Control"), _cache_control); - response->addHeader(F("ETag"), etag); - } - request->send(response); - } - } else { - request->send(404); - } -} diff --git a/lib/ESPAsyncWebServer/src/WebRequest.cpp b/lib/ESPAsyncWebServer/src/WebRequest.cpp deleted file mode 100644 index 36ec08faa..000000000 --- a/lib/ESPAsyncWebServer/src/WebRequest.cpp +++ /dev/null @@ -1,1082 +0,0 @@ -/* - Asynchronous WebServer library for Espressif MCUs - - Copyright (c) 2016 Hristo Gochkov. All rights reserved. - This file is part of the esp8266 core 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 -*/ -#include "ESPAsyncWebServer.h" -#include "WebResponseImpl.h" -#include "WebAuthentication.h" - -#ifndef ESP8266 -#define os_strlen strlen -#endif - -#define __is_param_char(c) ((c) && ((c) != '{') && ((c) != '[') && ((c) != '&') && ((c) != '=')) - -enum { PARSE_REQ_START, PARSE_REQ_HEADERS, PARSE_REQ_BODY, PARSE_REQ_END, PARSE_REQ_FAIL }; - -AsyncWebServerRequest::AsyncWebServerRequest(AsyncWebServer * s, AsyncClient * c) - : _client(c) - , _server(s) - , _handler(NULL) - , _response(NULL) - , _temp() - , _parseState(0) - , _version(0) - , _method(HTTP_ANY) - , _url() - , _host() - , _contentType() - , _boundary() - , _authorization() - , _reqconntype(RCT_HTTP) - , _isDigest(false) - , _isMultipart(false) - , _isPlainPost(false) - , _expectingContinue(false) - , _contentLength(0) - , _parsedLength(0) - , _params(LinkedList([](AsyncWebParameter * p) { delete p; })) - , _multiParseState(0) - , _boundaryPosition(0) - , _itemStartIndex(0) - , _itemSize(0) - , _itemName() - , _itemFilename() - , _itemType() - , _itemValue() - , _itemBuffer(0) - , _itemBufferIndex(0) - , _itemIsFile(false) - , _tempObject(NULL) { - c->onError( - [](void * r, AsyncClient * c, int8_t error) { - (void)c; - AsyncWebServerRequest * req = (AsyncWebServerRequest *)r; - req->_onError(error); - }, - this); - c->onAck( - [](void * r, AsyncClient * c, size_t len, uint32_t time) { - (void)c; - AsyncWebServerRequest * req = (AsyncWebServerRequest *)r; - req->_onAck(len, time); - }, - this); - c->onDisconnect( - [](void * r, AsyncClient * c) { - AsyncWebServerRequest * req = (AsyncWebServerRequest *)r; - req->_onDisconnect(); - delete c; - }, - this); - c->onTimeout( - [](void * r, AsyncClient * c, uint32_t time) { - (void)c; - AsyncWebServerRequest * req = (AsyncWebServerRequest *)r; - req->_onTimeout(time); - }, - this); - c->onData( - [](void * r, AsyncClient * c, void * buf, size_t len) { - (void)c; - AsyncWebServerRequest * req = (AsyncWebServerRequest *)r; - req->_onData(buf, len); - }, - this); - c->onPoll( - [](void * r, AsyncClient * c) { - (void)c; - AsyncWebServerRequest * req = (AsyncWebServerRequest *)r; - req->_onPoll(); - }, - this); -} - -AsyncWebServerRequest::~AsyncWebServerRequest() { - _headers.clear(); - - _params.free(); - _pathParams.clear(); - - _interestingHeaders.clear(); - - if (_response != NULL) { - delete _response; - } - - if (_tempObject != NULL) { - free(_tempObject); - } - - if (_tempFile) { - _tempFile.close(); - } - - if (_itemBuffer) { - free(_itemBuffer); - } -} - -void AsyncWebServerRequest::_onData(void * buf, size_t len) { - size_t i = 0; - while (true) { - if (_parseState < PARSE_REQ_BODY) { - // Find new line in buf - char * str = (char *)buf; - for (i = 0; i < len; i++) { - if (str[i] == '\n') { - break; - } - } - if (i == len) { // No new line, just add the buffer in _temp - char ch = str[len - 1]; - str[len - 1] = 0; - _temp.reserve(_temp.length() + len); - _temp.concat(str); - _temp.concat(ch); - } else { // Found new line - extract it and parse - str[i] = 0; // Terminate the string at the end of the line. - _temp.concat(str); - _temp.trim(); - _parseLine(); - if (++i < len) { - // Still have more buffer to process - buf = str + i; - len -= i; - continue; - } - } - } else if (_parseState == PARSE_REQ_BODY) { - // A handler should be already attached at this point in _parseLine function. - // If handler does nothing (_onRequest is NULL), we don't need to really parse the body. - const bool needParse = _handler && !_handler->isRequestHandlerTrivial(); - if (_isMultipart) { - if (needParse) { - size_t i; - for (i = 0; i < len; i++) { - _parseMultipartPostByte(((uint8_t *)buf)[i], i == len - 1); - _parsedLength++; - } - } else - _parsedLength += len; - } else { - if (_parsedLength == 0) { - if (_contentType.startsWith(F("application/x-www-form-urlencoded"))) { - _isPlainPost = true; - } else if (_contentType == F("text/plain") && __is_param_char(((char *)buf)[0])) { - size_t i = 0; - while (i < len && __is_param_char(((char *)buf)[i++])) - ; - if (i < len && ((char *)buf)[i - 1] == '=') { - _isPlainPost = true; - } - } - } - if (!_isPlainPost) { - //check if authenticated before calling the body - if (_handler) - _handler->handleBody(this, (uint8_t *)buf, len, _parsedLength, _contentLength); - _parsedLength += len; - } else if (needParse) { - size_t i; - for (i = 0; i < len; i++) { - _parsedLength++; - _parsePlainPostChar(((uint8_t *)buf)[i]); - } - } else { - _parsedLength += len; - } - } - if (_parsedLength == _contentLength) { - _parseState = PARSE_REQ_END; - //check if authenticated before calling handleRequest and request auth instead - if (_handler) - _handler->handleRequest(this); - else - send(501); - } - } - break; - } -} - -void AsyncWebServerRequest::_removeNotInterestingHeaders() { - if (std::any_of(std::begin(_interestingHeaders), std::end(_interestingHeaders), [](const String & str) { return str.equalsIgnoreCase(F("ANY")); })) - return; // nothing to do - - for (auto iter = std::begin(_headers); iter != std::end(_headers);) { - const auto name = iter->name(); - - if (std::none_of(std::begin(_interestingHeaders), std::end(_interestingHeaders), [&name](const String & str) { return str.equalsIgnoreCase(name); })) - iter = _headers.erase(iter); - else - iter++; - } -} - -void AsyncWebServerRequest::_onPoll() { - //os_printf("p\n"); - if (_response != NULL && _client != NULL && _client->canSend()) { - if (!_response->_finished()) { - _response->_ack(this, 0, 0); - } else { - AsyncWebServerResponse * r = _response; - _response = NULL; - delete r; - - _client->close(); - } - } -} - -void AsyncWebServerRequest::_onAck(size_t len, uint32_t time) { - //os_printf("a:%u:%u\n", len, time); - if (_response != NULL) { - if (!_response->_finished()) { - _response->_ack(this, len, time); - } else if (_response->_finished()) { - AsyncWebServerResponse * r = _response; - _response = NULL; - delete r; - - _client->close(); - } - } -} - -void AsyncWebServerRequest::_onError(int8_t error) { - (void)error; -} - -void AsyncWebServerRequest::_onTimeout(uint32_t time) { - (void)time; - //os_printf("TIMEOUT: %u, state: %s\n", time, _client->stateToString()); - _client->close(); -} - -void AsyncWebServerRequest::onDisconnect(ArDisconnectHandler fn) { - _onDisconnectfn = fn; -} - -void AsyncWebServerRequest::_onDisconnect() { - //os_printf("d\n"); - if (_onDisconnectfn) { - _onDisconnectfn(); - } - _server->_handleDisconnect(this); -} - -void AsyncWebServerRequest::_addParam(AsyncWebParameter * p) { - _params.add(p); -} - -void AsyncWebServerRequest::_addPathParam(const char * p) { - _pathParams.emplace_back(p); -} - -void AsyncWebServerRequest::_addGetParams(const String & params) { - size_t start = 0; - while (start < params.length()) { - int end = params.indexOf('&', start); - if (end < 0) - end = params.length(); - int equal = params.indexOf('=', start); - if (equal < 0 || equal > end) - equal = end; - String name = params.substring(start, equal); - String value = equal + 1 < end ? params.substring(equal + 1, end) : String(); - _addParam(new AsyncWebParameter(urlDecode(name), urlDecode(value))); - start = end + 1; - } -} - -bool AsyncWebServerRequest::_parseReqHead() { - // Split the head into method, url and version - int index = _temp.indexOf(' '); - String m = _temp.substring(0, index); - index = _temp.indexOf(' ', index + 1); - String u = _temp.substring(m.length() + 1, index); - _temp = _temp.substring(index + 1); - - if (m == F("GET")) { - _method = HTTP_GET; - } else if (m == F("POST")) { - _method = HTTP_POST; - } else if (m == F("DELETE")) { - _method = HTTP_DELETE; - } else if (m == F("PUT")) { - _method = HTTP_PUT; - } else if (m == F("PATCH")) { - _method = HTTP_PATCH; - } else if (m == F("HEAD")) { - _method = HTTP_HEAD; - } else if (m == F("OPTIONS")) { - _method = HTTP_OPTIONS; - } - - String g; - index = u.indexOf('?'); - if (index > 0) { - g = u.substring(index + 1); - u = u.substring(0, index); - } - _url = urlDecode(u); - _addGetParams(g); - - if (!_temp.startsWith(F("HTTP/1.0"))) - _version = 1; - - _temp = String(); - return true; -} - -bool strContains(const String & src, const String & find, bool mindcase = true) { - int pos = 0, i = 0; - const int slen = src.length(); - const int flen = find.length(); - - if (slen < flen) - return false; - while (pos <= (slen - flen)) { - for (i = 0; i < flen; i++) { - if (mindcase) { - if (src[pos + i] != find[i]) - i = flen + 1; // no match - } else if (tolower(src[pos + i]) != tolower(find[i])) { - i = flen + 1; // no match - } - } - if (i == flen) - return true; - pos++; - } - return false; -} - -bool AsyncWebServerRequest::_parseReqHeader() { - int index = _temp.indexOf(':'); - if (index) { - String name = _temp.substring(0, index); - String value = _temp.substring(index + 2); - if (name.equalsIgnoreCase("Host")) { - _host = value; - } else if (name.equalsIgnoreCase(F("Content-Type"))) { - _contentType = value.substring(0, value.indexOf(';')); - if (value.startsWith(F("multipart/"))) { - _boundary = value.substring(value.indexOf('=') + 1); - _boundary.replace(String('"'), String()); - _isMultipart = true; - } - } else if (name.equalsIgnoreCase(F("Content-Length"))) { - _contentLength = atoi(value.c_str()); - } else if (name.equalsIgnoreCase(F("Expect")) && value == F("100-continue")) { - _expectingContinue = true; - } else if (name.equalsIgnoreCase(F("Authorization"))) { - if (value.length() > 5 && value.substring(0, 5).equalsIgnoreCase(F("Basic"))) { - _authorization = value.substring(6); - } else if (value.length() > 6 && value.substring(0, 6).equalsIgnoreCase(F("Digest"))) { - _isDigest = true; - _authorization = value.substring(7); - } - } else { - if (name.equalsIgnoreCase(F("Upgrade")) && value.equalsIgnoreCase(F("websocket"))) { - // WebSocket request can be uniquely identified by header: [Upgrade: websocket] - _reqconntype = RCT_WS; - } else { - if (name.equalsIgnoreCase(F("Accept")) && strContains(value, F("text/event-stream"), false)) { - // WebEvent request can be uniquely identified by header: [Accept: text/event-stream] - _reqconntype = RCT_EVENT; - } - } - } - _headers.emplace_back(name, value); - } - _temp = String(); - return true; -} - -void AsyncWebServerRequest::_parsePlainPostChar(uint8_t data) { - if (data && (char)data != '&') - _temp += (char)data; - if (!data || (char)data == '&' || _parsedLength == _contentLength) { - String name = F("body"); - String value = _temp; - if (!_temp.startsWith(String('{')) && !_temp.startsWith(String('[')) && _temp.indexOf('=') > 0) { - name = _temp.substring(0, _temp.indexOf('=')); - value = _temp.substring(_temp.indexOf('=') + 1); - } - _addParam(new AsyncWebParameter(urlDecode(name), urlDecode(value), true)); - _temp = String(); - } -} - -void AsyncWebServerRequest::_handleUploadByte(uint8_t data, bool last) { - _itemBuffer[_itemBufferIndex++] = data; - - if (last || _itemBufferIndex == 1460) { - //check if authenticated before calling the upload - if (_handler) - _handler->handleUpload(this, _itemFilename, _itemSize - _itemBufferIndex, _itemBuffer, _itemBufferIndex, false); - _itemBufferIndex = 0; - } -} - -enum { - EXPECT_BOUNDARY, - PARSE_HEADERS, - WAIT_FOR_RETURN1, - EXPECT_FEED1, - EXPECT_DASH1, - EXPECT_DASH2, - BOUNDARY_OR_DATA, - DASH3_OR_RETURN2, - EXPECT_FEED2, - PARSING_FINISHED, - PARSE_ERROR -}; - -void AsyncWebServerRequest::_parseMultipartPostByte(uint8_t data, bool last) { -#define itemWriteByte(b) \ - do { \ - _itemSize++; \ - if (_itemIsFile) \ - _handleUploadByte(b, last); \ - else \ - _itemValue += (char)(b); \ - } while (0) - - if (!_parsedLength) { - _multiParseState = EXPECT_BOUNDARY; - _temp = String(); - _itemName = String(); - _itemFilename = String(); - _itemType = String(); - } - - if (_multiParseState == WAIT_FOR_RETURN1) { - if (data != '\r') { - itemWriteByte(data); - } else { - _multiParseState = EXPECT_FEED1; - } - } else if (_multiParseState == EXPECT_BOUNDARY) { - if (_parsedLength < 2 && data != '-') { - _multiParseState = PARSE_ERROR; - return; - } else if (_parsedLength - 2 < _boundary.length() && _boundary.c_str()[_parsedLength - 2] != data) { - _multiParseState = PARSE_ERROR; - return; - } else if (_parsedLength - 2 == _boundary.length() && data != '\r') { - _multiParseState = PARSE_ERROR; - return; - } else if (_parsedLength - 3 == _boundary.length()) { - if (data != '\n') { - _multiParseState = PARSE_ERROR; - return; - } - _multiParseState = PARSE_HEADERS; - _itemIsFile = false; - } - } else if (_multiParseState == PARSE_HEADERS) { - if ((char)data != '\r' && (char)data != '\n') - _temp += (char)data; - if ((char)data == '\n') { - if (_temp.length()) { - if (_temp.length() > 12 && _temp.substring(0, 12).equalsIgnoreCase(F("Content-Type"))) { - _itemType = _temp.substring(14); - _itemIsFile = true; - } else if (_temp.length() > 19 && _temp.substring(0, 19).equalsIgnoreCase(F("Content-Disposition"))) { - _temp = _temp.substring(_temp.indexOf(';') + 2); - while (_temp.indexOf(';') > 0) { - String name = _temp.substring(0, _temp.indexOf('=')); - String nameVal = _temp.substring(_temp.indexOf('=') + 2, _temp.indexOf(';') - 1); - if (name == F("name")) { - _itemName = nameVal; - } else if (name == F("filename")) { - _itemFilename = nameVal; - _itemIsFile = true; - } - _temp = _temp.substring(_temp.indexOf(';') + 2); - } - String name = _temp.substring(0, _temp.indexOf('=')); - String nameVal = _temp.substring(_temp.indexOf('=') + 2, _temp.length() - 1); - if (name == F("name")) { - _itemName = nameVal; - } else if (name == F("filename")) { - _itemFilename = nameVal; - _itemIsFile = true; - } - } - _temp = String(); - } else { - _multiParseState = WAIT_FOR_RETURN1; - //value starts from here - _itemSize = 0; - _itemStartIndex = _parsedLength; - _itemValue = String(); - if (_itemIsFile) { - if (_itemBuffer) - free(_itemBuffer); - _itemBuffer = (uint8_t *)malloc(1460); - if (_itemBuffer == NULL) { - _multiParseState = PARSE_ERROR; - return; - } - _itemBufferIndex = 0; - } - } - } - } else if (_multiParseState == EXPECT_FEED1) { - if (data != '\n') { - _multiParseState = WAIT_FOR_RETURN1; - itemWriteByte('\r'); - _parseMultipartPostByte(data, last); - } else { - _multiParseState = EXPECT_DASH1; - } - } else if (_multiParseState == EXPECT_DASH1) { - if (data != '-') { - _multiParseState = WAIT_FOR_RETURN1; - itemWriteByte('\r'); - itemWriteByte('\n'); - _parseMultipartPostByte(data, last); - } else { - _multiParseState = EXPECT_DASH2; - } - } else if (_multiParseState == EXPECT_DASH2) { - if (data != '-') { - _multiParseState = WAIT_FOR_RETURN1; - itemWriteByte('\r'); - itemWriteByte('\n'); - itemWriteByte('-'); - _parseMultipartPostByte(data, last); - } else { - _multiParseState = BOUNDARY_OR_DATA; - _boundaryPosition = 0; - } - } else if (_multiParseState == BOUNDARY_OR_DATA) { - if (_boundaryPosition < _boundary.length() && _boundary.c_str()[_boundaryPosition] != data) { - _multiParseState = WAIT_FOR_RETURN1; - itemWriteByte('\r'); - itemWriteByte('\n'); - itemWriteByte('-'); - itemWriteByte('-'); - uint8_t i; - for (i = 0; i < _boundaryPosition; i++) - itemWriteByte(_boundary.c_str()[i]); - _parseMultipartPostByte(data, last); - } else if (_boundaryPosition == _boundary.length() - 1) { - _multiParseState = DASH3_OR_RETURN2; - if (!_itemIsFile) { - _addParam(new AsyncWebParameter(_itemName, _itemValue, true)); - } else { - if (_itemSize) { - //check if authenticated before calling the upload - if (_handler) - _handler->handleUpload(this, _itemFilename, _itemSize - _itemBufferIndex, _itemBuffer, _itemBufferIndex, true); - _itemBufferIndex = 0; - _addParam(new AsyncWebParameter(_itemName, _itemFilename, true, true, _itemSize)); - } - free(_itemBuffer); - _itemBuffer = NULL; - } - - } else { - _boundaryPosition++; - } - } else if (_multiParseState == DASH3_OR_RETURN2) { - if (data == '-' && (_contentLength - _parsedLength - 4) != 0) { - //os_printf("ERROR: The parser got to the end of the POST but is expecting %u bytes more!\nDrop an issue so we can have more info on the matter!\n", _contentLength - _parsedLength - 4); - _contentLength = _parsedLength + 4; //lets close the request gracefully - } - if (data == '\r') { - _multiParseState = EXPECT_FEED2; - } else if (data == '-' && _contentLength == (_parsedLength + 4)) { - _multiParseState = PARSING_FINISHED; - } else { - _multiParseState = WAIT_FOR_RETURN1; - itemWriteByte('\r'); - itemWriteByte('\n'); - itemWriteByte('-'); - itemWriteByte('-'); - uint8_t i; - for (i = 0; i < _boundary.length(); i++) - itemWriteByte(_boundary.c_str()[i]); - _parseMultipartPostByte(data, last); - } - } else if (_multiParseState == EXPECT_FEED2) { - if (data == '\n') { - _multiParseState = PARSE_HEADERS; - _itemIsFile = false; - } else { - _multiParseState = WAIT_FOR_RETURN1; - itemWriteByte('\r'); - itemWriteByte('\n'); - itemWriteByte('-'); - itemWriteByte('-'); - uint8_t i; - for (i = 0; i < _boundary.length(); i++) - itemWriteByte(_boundary.c_str()[i]); - itemWriteByte('\r'); - _parseMultipartPostByte(data, last); - } - } -} - -void AsyncWebServerRequest::_parseLine() { - if (_parseState == PARSE_REQ_START) { - if (!_temp.length()) { - _parseState = PARSE_REQ_FAIL; - _client->close(); - } else { - _parseReqHead(); - _parseState = PARSE_REQ_HEADERS; - } - return; - } - - if (_parseState == PARSE_REQ_HEADERS) { - if (!_temp.length()) { - //end of headers - _server->_rewriteRequest(this); - _server->_attachHandler(this); - _removeNotInterestingHeaders(); - if (_expectingContinue) { - String response = F("HTTP/1.1 100 Continue\r\n\r\n"); - _client->write(response.c_str(), response.length()); - } - //check handler for authentication - if (_contentLength) { - _parseState = PARSE_REQ_BODY; - } else { - _parseState = PARSE_REQ_END; - if (_handler) - _handler->handleRequest(this); - else - send(501); - } - } else - _parseReqHeader(); - } -} - -size_t AsyncWebServerRequest::headers() const { - return _headers.size(); -} - -bool AsyncWebServerRequest::hasHeader(const String & name) const { - for (const auto & h : _headers) { - if (h.name().equalsIgnoreCase(name)) { - return true; - } - } - return false; -} - -bool AsyncWebServerRequest::hasHeader(const __FlashStringHelper * data) const { - return hasHeader(String(data)); -} - -AsyncWebHeader * AsyncWebServerRequest::getHeader(const String & name) { - auto iter = std::find_if(std::begin(_headers), std::end(_headers), [&name](const AsyncWebHeader & header) { return header.name().equalsIgnoreCase(name); }); - - if (iter == std::end(_headers)) - return nullptr; - - return &(*iter); -} - -const AsyncWebHeader * AsyncWebServerRequest::getHeader(const String & name) const { - auto iter = std::find_if(std::begin(_headers), std::end(_headers), [&name](const AsyncWebHeader & header) { return header.name().equalsIgnoreCase(name); }); - - if (iter == std::end(_headers)) - return nullptr; - - return &(*iter); -} - -AsyncWebHeader * AsyncWebServerRequest::getHeader(const __FlashStringHelper * data) { - PGM_P p = reinterpret_cast(data); - size_t n = strlen_P(p); - char * name = (char *)malloc(n + 1); - if (name) { - strcpy_P(name, p); - AsyncWebHeader * result = getHeader(String(name)); - free(name); - return result; - } else { - return nullptr; - } -} - -const AsyncWebHeader * AsyncWebServerRequest::getHeader(const __FlashStringHelper * data) const { - PGM_P p = reinterpret_cast(data); - size_t n = strlen_P(p); - char * name = (char *)malloc(n + 1); - if (name) { - strcpy_P(name, p); - const AsyncWebHeader * result = getHeader(String(name)); - free(name); - return result; - } else { - return nullptr; - } -} - -AsyncWebHeader * AsyncWebServerRequest::getHeader(size_t num) { - if (num >= _headers.size()) - return nullptr; - return &(*std::next(std::begin(_headers), num)); -} - -const AsyncWebHeader * AsyncWebServerRequest::getHeader(size_t num) const { - if (num >= _headers.size()) - return nullptr; - return &(*std::next(std::begin(_headers), num)); -} - -size_t AsyncWebServerRequest::params() const { - return _params.length(); -} - -bool AsyncWebServerRequest::hasParam(const String & name, bool post, bool file) const { - for (const auto & p : _params) { - if (p->name() == name && p->isPost() == post && p->isFile() == file) { - return true; - } - } - return false; -} - -bool AsyncWebServerRequest::hasParam(const __FlashStringHelper * data, bool post, bool file) const { - return hasParam(String(data).c_str(), post, file); -} - -AsyncWebParameter * AsyncWebServerRequest::getParam(const String & name, bool post, bool file) const { - for (const auto & p : _params) { - if (p->name() == name && p->isPost() == post && p->isFile() == file) { - return p; - } - } - return nullptr; -} - -AsyncWebParameter * AsyncWebServerRequest::getParam(const __FlashStringHelper * data, bool post, bool file) const { - return getParam(String(data).c_str(), post, file); -} - -AsyncWebParameter * AsyncWebServerRequest::getParam(size_t num) const { - auto param = _params.nth(num); - return param ? *param : nullptr; -} - -void AsyncWebServerRequest::addInterestingHeader(const String & name) { - if (std::none_of(std::begin(_interestingHeaders), std::end(_interestingHeaders), [&name](const String & str) { return str.equalsIgnoreCase(name); })) - _interestingHeaders.push_back(name); -} - -void AsyncWebServerRequest::send(AsyncWebServerResponse * response) { - _response = response; - if (_response == NULL) { - _client->close(true); - _onDisconnect(); - return; - } - if (!_response->_sourceValid()) { - delete response; - _response = NULL; - send(500); - } else { - _client->setRxTimeout(0); - _response->_respond(this); - } -} - -AsyncWebServerResponse * AsyncWebServerRequest::beginResponse(int code, const String & contentType, const String & content) { - return new AsyncBasicResponse(code, contentType, content); -} - -AsyncWebServerResponse * -AsyncWebServerRequest::beginResponse(FS & fs, const String & path, const String & contentType, bool download, AwsTemplateProcessor callback) { - if (fs.exists(path) || (!download && fs.exists(path + F(".gz")))) - return new AsyncFileResponse(fs, path, contentType, download, callback); - return NULL; -} - -AsyncWebServerResponse * -AsyncWebServerRequest::beginResponse(File content, const String & path, const String & contentType, bool download, AwsTemplateProcessor callback) { - if (content == true) - return new AsyncFileResponse(content, path, contentType, download, callback); - return NULL; -} - -AsyncWebServerResponse * AsyncWebServerRequest::beginResponse(Stream & stream, const String & contentType, size_t len, AwsTemplateProcessor callback) { - return new AsyncStreamResponse(stream, contentType, len, callback); -} - -AsyncWebServerResponse * -AsyncWebServerRequest::beginResponse(const String & contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback) { - return new AsyncCallbackResponse(contentType, len, callback, templateCallback); -} - -AsyncWebServerResponse * -AsyncWebServerRequest::beginChunkedResponse(const String & contentType, AwsResponseFiller callback, AwsTemplateProcessor templateCallback) { - if (_version) - return new AsyncChunkedResponse(contentType, callback, templateCallback); - return new AsyncCallbackResponse(contentType, 0, callback, templateCallback); -} - -AsyncResponseStream * AsyncWebServerRequest::beginResponseStream(const String & contentType, size_t bufferSize) { - return new AsyncResponseStream(contentType, bufferSize); -} - -AsyncWebServerResponse * -AsyncWebServerRequest::beginResponse_P(int code, const String & contentType, const uint8_t * content, size_t len, AwsTemplateProcessor callback) { - return new AsyncProgmemResponse(code, contentType, content, len, callback); -} - -AsyncWebServerResponse * AsyncWebServerRequest::beginResponse_P(int code, const String & contentType, PGM_P content, AwsTemplateProcessor callback) { - return beginResponse_P(code, contentType, (const uint8_t *)content, strlen_P(content), callback); -} - -void AsyncWebServerRequest::send(int code, const String & contentType, const String & content) { - send(beginResponse(code, contentType, content)); -} - -void AsyncWebServerRequest::send(FS & fs, const String & path, const String & contentType, bool download, AwsTemplateProcessor callback) { - if (fs.exists(path) || (!download && fs.exists(path + F(".gz")))) { - send(beginResponse(fs, path, contentType, download, callback)); - } else - send(404); -} - -void AsyncWebServerRequest::send(File content, const String & path, const String & contentType, bool download, AwsTemplateProcessor callback) { - if (content == true) { - send(beginResponse(content, path, contentType, download, callback)); - } else - send(404); -} - -void AsyncWebServerRequest::send(Stream & stream, const String & contentType, size_t len, AwsTemplateProcessor callback) { - send(beginResponse(stream, contentType, len, callback)); -} - -void AsyncWebServerRequest::send(const String & contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback) { - send(beginResponse(contentType, len, callback, templateCallback)); -} - -void AsyncWebServerRequest::sendChunked(const String & contentType, AwsResponseFiller callback, AwsTemplateProcessor templateCallback) { - send(beginChunkedResponse(contentType, callback, templateCallback)); -} - -void AsyncWebServerRequest::send_P(int code, const String & contentType, const uint8_t * content, size_t len, AwsTemplateProcessor callback) { - send(beginResponse_P(code, contentType, content, len, callback)); -} - -void AsyncWebServerRequest::send_P(int code, const String & contentType, PGM_P content, AwsTemplateProcessor callback) { - send(beginResponse_P(code, contentType, content, callback)); -} - -void AsyncWebServerRequest::redirect(const String & url) { - AsyncWebServerResponse * response = beginResponse(302); - response->addHeader(F("Location"), url); - send(response); -} - -bool AsyncWebServerRequest::authenticate(const char * username, const char * password, const char * realm, bool passwordIsHash) { - if (_authorization.length()) { - if (_isDigest) - return checkDigestAuthentication(_authorization.c_str(), methodToString(), username, password, realm, passwordIsHash, NULL, NULL, NULL); - else if (!passwordIsHash) - return checkBasicAuthentication(_authorization.c_str(), username, password); - else - return _authorization.equals(password); - } - return false; -} - -bool AsyncWebServerRequest::authenticate(const char * hash) { - if (!_authorization.length() || hash == NULL) - return false; - - if (_isDigest) { - String hStr = String(hash); - int separator = hStr.indexOf(':'); - if (separator <= 0) - return false; - String username = hStr.substring(0, separator); - hStr = hStr.substring(separator + 1); - separator = hStr.indexOf(':'); - if (separator <= 0) - return false; - String realm = hStr.substring(0, separator); - hStr = hStr.substring(separator + 1); - return checkDigestAuthentication(_authorization.c_str(), methodToString(), username.c_str(), hStr.c_str(), realm.c_str(), true, NULL, NULL, NULL); - } - - return (_authorization.equals(hash)); -} - -void AsyncWebServerRequest::requestAuthentication(const char * realm, bool isDigest) { - AsyncWebServerResponse * r = beginResponse(401); - if (!isDigest && realm == NULL) { - r->addHeader(F("WWW-Authenticate"), F("Basic realm=\"Login Required\"")); - } else if (!isDigest) { - String header = F("Basic realm=\""); - header.concat(realm); - header += '"'; - r->addHeader(F("WWW-Authenticate"), header); - } else { - String header = F("Digest "); - header.concat(requestDigestAuthentication(realm)); - r->addHeader(F("WWW-Authenticate"), header); - } - send(r); -} - -bool AsyncWebServerRequest::hasArg(const char * name) const { - for (const auto & arg : _params) { - if (arg->name() == name) { - return true; - } - } - return false; -} - -bool AsyncWebServerRequest::hasArg(const __FlashStringHelper * data) const { - return hasArg(String(data).c_str()); -} - - -const String & AsyncWebServerRequest::arg(const String & name) const { - for (const auto & arg : _params) { - if (arg->name() == name) { - return arg->value(); - } - } - return emptyString; -} - -const String & AsyncWebServerRequest::arg(const __FlashStringHelper * data) const { - return arg(String(data).c_str()); -} - -const String & AsyncWebServerRequest::arg(size_t i) const { - return getParam(i)->value(); -} - -const String & AsyncWebServerRequest::argName(size_t i) const { - return getParam(i)->name(); -} - -const String & AsyncWebServerRequest::pathArg(size_t i) const { - return i < _pathParams.size() ? _pathParams[i] : emptyString; -} - -const String & AsyncWebServerRequest::header(const char * name) const { - const AsyncWebHeader * h = getHeader(String(name)); - return h ? h->value() : emptyString; -} - -const String & AsyncWebServerRequest::header(const __FlashStringHelper * data) const { - return header(String(data).c_str()); -}; - - -const String & AsyncWebServerRequest::header(size_t i) const { - const AsyncWebHeader * h = getHeader(i); - return h ? h->value() : emptyString; -} - -const String & AsyncWebServerRequest::headerName(size_t i) const { - const AsyncWebHeader * h = getHeader(i); - return h ? h->name() : emptyString; -} - -String AsyncWebServerRequest::urlDecode(const String & text) const { - char temp[] = "0x00"; - unsigned int len = text.length(); - unsigned int i = 0; - String decoded = String(); - decoded.reserve(len); // Allocate the string internal buffer - never longer from source text - 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.concat(decodedChar); - } - return decoded; -} - - -const __FlashStringHelper * AsyncWebServerRequest::methodToString() const { - if (_method == HTTP_ANY) - return F("ANY"); - else if (_method & HTTP_GET) - return F("GET"); - else if (_method & HTTP_POST) - return F("POST"); - else if (_method & HTTP_DELETE) - return F("DELETE"); - else if (_method & HTTP_PUT) - return F("PUT"); - else if (_method & HTTP_PATCH) - return F("PATCH"); - else if (_method & HTTP_HEAD) - return F("HEAD"); - else if (_method & HTTP_OPTIONS) - return F("OPTIONS"); - return F("UNKNOWN"); -} - -const __FlashStringHelper * AsyncWebServerRequest::requestedConnTypeToString() const { - switch (_reqconntype) { - case RCT_NOT_USED: - return F("RCT_NOT_USED"); - case RCT_DEFAULT: - return F("RCT_DEFAULT"); - case RCT_HTTP: - return F("RCT_HTTP"); - case RCT_WS: - return F("RCT_WS"); - case RCT_EVENT: - return F("RCT_EVENT"); - default: - return F("ERROR"); - } -} - -bool AsyncWebServerRequest::isExpectedRequestedConnType(RequestedConnectionType erct1, RequestedConnectionType erct2, RequestedConnectionType erct3) { - bool res = false; - if ((erct1 != RCT_NOT_USED) && (erct1 == _reqconntype)) - res = true; - if ((erct2 != RCT_NOT_USED) && (erct2 == _reqconntype)) - res = true; - if ((erct3 != RCT_NOT_USED) && (erct3 == _reqconntype)) - res = true; - return res; -} diff --git a/lib/ESPAsyncWebServer/src/WebResponseImpl.h b/lib/ESPAsyncWebServer/src/WebResponseImpl.h deleted file mode 100644 index a0ca90c75..000000000 --- a/lib/ESPAsyncWebServer/src/WebResponseImpl.h +++ /dev/null @@ -1,166 +0,0 @@ -/* - Asynchronous WebServer library for Espressif MCUs - - Copyright (c) 2016 Hristo Gochkov. All rights reserved. - This file is part of the esp8266 core 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 -*/ -#ifndef ASYNCWEBSERVERRESPONSEIMPL_H_ -#define ASYNCWEBSERVERRESPONSEIMPL_H_ - -#ifdef Arduino_h -// arduino is not compatible with std::vector -#undef min -#undef max -#endif -#include -#include - -// It is possible to restore these defines, but one can use _min and _max instead. Or std::min, std::max. - -class AsyncBasicResponse : public AsyncWebServerResponse { - private: - String _content; - - public: - AsyncBasicResponse(int code, const String & contentType = String(), const String & content = String()); - void _respond(AsyncWebServerRequest * request); - size_t _ack(AsyncWebServerRequest * request, size_t len, uint32_t time); - bool _sourceValid() const { - return true; - } -}; - -class AsyncAbstractResponse : public AsyncWebServerResponse { - private: - String _head; - // Data is inserted into cache at begin(). - // This is inefficient with vector, but if we use some other container, - // we won't be able to access it as contiguous array of bytes when reading from it, - // so by gaining performance in one place, we'll lose it in another. - std::vector _cache; - size_t _readDataFromCacheOrContent(uint8_t * data, const size_t len); - size_t _fillBufferAndProcessTemplates(uint8_t * buf, size_t maxLen); - - protected: - AwsTemplateProcessor _callback; - - public: - AsyncAbstractResponse(AwsTemplateProcessor callback = nullptr); - void _respond(AsyncWebServerRequest * request); - size_t _ack(AsyncWebServerRequest * request, size_t len, uint32_t time); - bool _sourceValid() const { - return false; - } - virtual size_t _fillBuffer(uint8_t * buf __attribute__((unused)), size_t maxLen __attribute__((unused))) { - return 0; - } -}; - -#ifndef TEMPLATE_PLACEHOLDER -#define TEMPLATE_PLACEHOLDER '%' -#endif - -#define TEMPLATE_PARAM_NAME_LENGTH 32 -class AsyncFileResponse : public AsyncAbstractResponse { - using File = fs::File; - using FS = fs::FS; - - private: - File _content; - String _path; - void _setContentType(const String & path); - - public: - AsyncFileResponse(FS & fs, const String & path, const String & contentType = String(), bool download = false, AwsTemplateProcessor callback = nullptr); - AsyncFileResponse(File content, const String & path, const String & contentType = String(), bool download = false, AwsTemplateProcessor callback = nullptr); - ~AsyncFileResponse(); - bool _sourceValid() const { - return !!(_content); - } - virtual size_t _fillBuffer(uint8_t * buf, size_t maxLen) override; -}; - -class AsyncStreamResponse : public AsyncAbstractResponse { - private: - Stream * _content; - - public: - AsyncStreamResponse(Stream & stream, const String & contentType, size_t len, AwsTemplateProcessor callback = nullptr); - bool _sourceValid() const { - return !!(_content); - } - virtual size_t _fillBuffer(uint8_t * buf, size_t maxLen) override; -}; - -class AsyncCallbackResponse : public AsyncAbstractResponse { - private: - AwsResponseFiller _content; - size_t _filledLength; - - public: - AsyncCallbackResponse(const String & contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback = nullptr); - bool _sourceValid() const { - return !!(_content); - } - virtual size_t _fillBuffer(uint8_t * buf, size_t maxLen) override; -}; - -class AsyncChunkedResponse : public AsyncAbstractResponse { - private: - AwsResponseFiller _content; - size_t _filledLength; - - public: - AsyncChunkedResponse(const String & contentType, AwsResponseFiller callback, AwsTemplateProcessor templateCallback = nullptr); - bool _sourceValid() const { - return !!(_content); - } - virtual size_t _fillBuffer(uint8_t * buf, size_t maxLen) override; -}; - -class AsyncProgmemResponse : public AsyncAbstractResponse { - private: - const uint8_t * _content; - size_t _readLength; - - public: - AsyncProgmemResponse(int code, const String & contentType, const uint8_t * content, size_t len, AwsTemplateProcessor callback = nullptr); - bool _sourceValid() const { - return true; - } - virtual size_t _fillBuffer(uint8_t * buf, size_t maxLen) override; -}; - -class cbuf; - -class AsyncResponseStream : public AsyncAbstractResponse, public Print { - private: - std::unique_ptr _content; - - public: - AsyncResponseStream(const String & contentType, size_t bufferSize); - ~AsyncResponseStream(); - bool _sourceValid() const { - return (_state < RESPONSE_END); - } - virtual size_t _fillBuffer(uint8_t * buf, size_t maxLen) override; - size_t write(const uint8_t * data, size_t len); - size_t write(uint8_t data); - using Print::write; -}; - -#endif /* ASYNCWEBSERVERRESPONSEIMPL_H_ */ diff --git a/lib/ESPAsyncWebServer/src/WebResponses.cpp b/lib/ESPAsyncWebServer/src/WebResponses.cpp deleted file mode 100644 index 5bdceab50..000000000 --- a/lib/ESPAsyncWebServer/src/WebResponses.cpp +++ /dev/null @@ -1,781 +0,0 @@ -/* - Asynchronous WebServer library for Espressif MCUs - - Copyright (c) 2016 Hristo Gochkov. All rights reserved. - This file is part of the esp8266 core 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 -*/ -#include "ESPAsyncWebServer.h" -#include "WebResponseImpl.h" -#include "cbuf.h" - -// Since ESP8266 does not link memchr by default, here's its implementation. -void * memchr(void * ptr, int ch, size_t count) { - unsigned char * p = static_cast(ptr); - while (count--) - if (*p++ == static_cast(ch)) - return --p; - return nullptr; -} - - -/* - * Abstract Response - * */ -const char * AsyncWebServerResponse::_responseCodeToString(int code) { - return reinterpret_cast(responseCodeToString(code)); -} - -const __FlashStringHelper * AsyncWebServerResponse::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(""); - } -} - -AsyncWebServerResponse::AsyncWebServerResponse() - : _code(0) - , _contentType() - , _contentLength(0) - , _sendContentLength(true) - , _chunked(false) - , _headLength(0) - , _sentLength(0) - , _ackedLength(0) - , _writtenLength(0) - , _state(RESPONSE_SETUP) { - for (const auto & header : DefaultHeaders::Instance()) { - _headers.emplace_back(header); - } -} - -AsyncWebServerResponse::~AsyncWebServerResponse() = default; - -void AsyncWebServerResponse::setCode(int code) { - if (_state == RESPONSE_SETUP) - _code = code; -} - -void AsyncWebServerResponse::setContentLength(size_t len) { - if (_state == RESPONSE_SETUP) - _contentLength = len; -} - -void AsyncWebServerResponse::setContentType(const String & type) { - if (_state == RESPONSE_SETUP) - _contentType = type; -} - -void AsyncWebServerResponse::addHeader(const String & name, const String & value) { - _headers.emplace_back(name, value); -} - -String AsyncWebServerResponse::_assembleHead(uint8_t version) { - if (version) { - addHeader(F("Accept-Ranges"), F("none")); - if (_chunked) - addHeader(F("Transfer-Encoding"), F("chunked")); - } - String out = String(); - int bufSize = 300; - char buf[bufSize]; - - snprintf_P(buf, bufSize, PSTR("HTTP/1.%d %d %s\r\n"), version, _code, _responseCodeToString(_code)); - out.concat(buf); - - if (_sendContentLength) { - snprintf_P(buf, bufSize, PSTR("Content-Length: %d\r\n"), _contentLength); - out.concat(buf); - } - if (_contentType.length()) { - snprintf_P(buf, bufSize, PSTR("Content-Type: %s\r\n"), _contentType.c_str()); - out.concat(buf); - } - - for (const auto & header : _headers) { - snprintf_P(buf, bufSize, PSTR("%s: %s\r\n"), header.name().c_str(), header.value().c_str()); - out.concat(buf); - } - _headers.clear(); - - out.concat(F("\r\n")); - _headLength = out.length(); - return out; -} - -bool AsyncWebServerResponse::_started() const { - return _state > RESPONSE_SETUP; -} -bool AsyncWebServerResponse::_finished() const { - return _state > RESPONSE_WAIT_ACK; -} -bool AsyncWebServerResponse::_failed() const { - return _state == RESPONSE_FAILED; -} -bool AsyncWebServerResponse::_sourceValid() const { - return false; -} -void AsyncWebServerResponse::_respond(AsyncWebServerRequest * request) { - _state = RESPONSE_END; - request->client()->close(); -} -size_t AsyncWebServerResponse::_ack(AsyncWebServerRequest * request, size_t len, uint32_t time) { - (void)request; - (void)len; - (void)time; - return 0; -} - -/* - * String/Code Response - * */ -AsyncBasicResponse::AsyncBasicResponse(int code, const String & contentType, const String & content) { - _code = code; - _content = content; - _contentType = contentType; - if (_content.length()) { - _contentLength = _content.length(); - if (!_contentType.length()) - _contentType = F("text/plain"); - } - addHeader(F("Connection"), F("close")); -} - -void AsyncBasicResponse::_respond(AsyncWebServerRequest * request) { - _state = RESPONSE_HEADERS; - String out = _assembleHead(request->version()); - size_t outLen = out.length(); - size_t space = request->client()->space(); - if (!_contentLength && space >= outLen) { - _writtenLength += request->client()->write(out.c_str(), outLen); - _state = RESPONSE_WAIT_ACK; - } else if (_contentLength && space >= outLen + _contentLength) { - out += _content; - outLen += _contentLength; - _writtenLength += request->client()->write(out.c_str(), outLen); - _state = RESPONSE_WAIT_ACK; - } else if (space && space < outLen) { - String partial = out.substring(0, space); - _content = out.substring(space) + _content; - _contentLength += outLen - space; - _writtenLength += request->client()->write(partial.c_str(), partial.length()); - _state = RESPONSE_CONTENT; - } else if (space > outLen && space < (outLen + _contentLength)) { - size_t shift = space - outLen; - outLen += shift; - _sentLength += shift; - out += _content.substring(0, shift); - _content = _content.substring(shift); - _writtenLength += request->client()->write(out.c_str(), outLen); - _state = RESPONSE_CONTENT; - } else { - _content = out + _content; - _contentLength += outLen; - _state = RESPONSE_CONTENT; - } -} - -size_t AsyncBasicResponse::_ack(AsyncWebServerRequest * request, size_t len, uint32_t time) { - (void)time; - _ackedLength += len; - if (_state == RESPONSE_CONTENT) { - size_t available = _contentLength - _sentLength; - size_t space = request->client()->space(); - //we can fit in this packet - if (space > available) { - _writtenLength += request->client()->write(_content.c_str(), available); - _content = String(); - _state = RESPONSE_WAIT_ACK; - return available; - } - //send some data, the rest on ack - String out = _content.substring(0, space); - _content = _content.substring(space); - _sentLength += space; - _writtenLength += request->client()->write(out.c_str(), space); - return space; - } else if (_state == RESPONSE_WAIT_ACK) { - if (_ackedLength >= _writtenLength) { - _state = RESPONSE_END; - } - } - return 0; -} - - -/* - * Abstract Response - * */ - -AsyncAbstractResponse::AsyncAbstractResponse(AwsTemplateProcessor callback) - : _callback(callback) { - // In case of template processing, we're unable to determine real response size - if (callback) { - _contentLength = 0; - _sendContentLength = false; - _chunked = true; - } -} - -void AsyncAbstractResponse::_respond(AsyncWebServerRequest * request) { - addHeader(F("Connection"), F("close")); - _head = _assembleHead(request->version()); - _state = RESPONSE_HEADERS; - _ack(request, 0, 0); -} - -size_t AsyncAbstractResponse::_ack(AsyncWebServerRequest * request, size_t len, uint32_t time) { - (void)time; - if (!_sourceValid()) { - _state = RESPONSE_FAILED; - request->client()->close(); - return 0; - } - _ackedLength += len; - size_t space = request->client()->space(); - - size_t headLen = _head.length(); - if (_state == RESPONSE_HEADERS) { - if (space >= headLen) { - _state = RESPONSE_CONTENT; - space -= headLen; - } else { - String out = _head.substring(0, space); - _head = _head.substring(space); - _writtenLength += request->client()->write(out.c_str(), out.length()); - return out.length(); - } - } - - if (_state == RESPONSE_CONTENT) { - size_t outLen; - if (_chunked) { - if (space <= 8) { - return 0; - } - outLen = space; - } else if (!_sendContentLength) { - outLen = space; - } else { - outLen = ((_contentLength - _sentLength) > space) ? space : (_contentLength - _sentLength); - } - - uint8_t * buf = (uint8_t *)malloc(outLen + headLen); - if (!buf) { - // os_printf("_ack malloc %d failed\n", outLen+headLen); - return 0; - } - - if (headLen) { - memcpy(buf, _head.c_str(), _head.length()); - } - - size_t readLen = 0; - - if (_chunked) { - // HTTP 1.1 allows leading zeros in chunk length. Or spaces may be added. - // See RFC2616 sections 2, 3.6.1. - readLen = _fillBufferAndProcessTemplates(buf + headLen + 6, outLen - 8); - if (readLen == RESPONSE_TRY_AGAIN) { - free(buf); - return 0; - } - outLen = sprintf_P((char *)buf + headLen, PSTR("%x"), readLen) + headLen; - while (outLen < headLen + 4) - buf[outLen++] = ' '; - buf[outLen++] = '\r'; - buf[outLen++] = '\n'; - outLen += readLen; - buf[outLen++] = '\r'; - buf[outLen++] = '\n'; - } else { - readLen = _fillBufferAndProcessTemplates(buf + headLen, outLen); - if (readLen == RESPONSE_TRY_AGAIN) { - free(buf); - return 0; - } - outLen = readLen + headLen; - } - - if (headLen) { - _head = String(); - } - - if (outLen) { - _writtenLength += request->client()->write((const char *)buf, outLen); - } - - if (_chunked) { - _sentLength += readLen; - } else { - _sentLength += outLen - headLen; - } - - free(buf); - - if ((_chunked && readLen == 0) || (!_sendContentLength && outLen == 0) || (!_chunked && _sentLength == _contentLength)) { - _state = RESPONSE_WAIT_ACK; - } - return outLen; - - } else if (_state == RESPONSE_WAIT_ACK) { - if (!_sendContentLength || _ackedLength >= _writtenLength) { - _state = RESPONSE_END; - if (!_chunked && !_sendContentLength) - request->client()->close(true); - } - } - return 0; -} - -size_t AsyncAbstractResponse::_readDataFromCacheOrContent(uint8_t * data, const size_t len) { - // If we have something in cache, copy it to buffer - const size_t readFromCache = std::min(len, _cache.size()); - if (readFromCache) { - memcpy(data, _cache.data(), readFromCache); - _cache.erase(_cache.begin(), _cache.begin() + readFromCache); - } - // If we need to read more... - const size_t needFromFile = len - readFromCache; - const size_t readFromContent = _fillBuffer(data + readFromCache, needFromFile); - return readFromCache + readFromContent; -} - -size_t AsyncAbstractResponse::_fillBufferAndProcessTemplates(uint8_t * data, size_t len) { - if (!_callback) - return _fillBuffer(data, len); - - const size_t originalLen = len; - len = _readDataFromCacheOrContent(data, len); - // Now we've read 'len' bytes, either from cache or from file - // Search for template placeholders - uint8_t * pTemplateStart = data; - while ((pTemplateStart < &data[len]) - && (pTemplateStart = (uint8_t *)memchr(pTemplateStart, TEMPLATE_PLACEHOLDER, &data[len - 1] - pTemplateStart + 1))) { // data[0] ... data[len - 1] - uint8_t * pTemplateEnd = - (pTemplateStart < &data[len - 1]) ? (uint8_t *)memchr(pTemplateStart + 1, TEMPLATE_PLACEHOLDER, &data[len - 1] - pTemplateStart) : nullptr; - // temporary buffer to hold parameter name - uint8_t buf[TEMPLATE_PARAM_NAME_LENGTH + 1]; - String paramName; - // If closing placeholder is found: - if (pTemplateEnd) { - // prepare argument to callback - const size_t paramNameLength = std::min((size_t)sizeof(buf) - 1, (size_t)(pTemplateEnd - pTemplateStart - 1)); - if (paramNameLength) { - memcpy(buf, pTemplateStart + 1, paramNameLength); - buf[paramNameLength] = 0; - paramName = String(reinterpret_cast(buf)); - } else { // double percent sign encountered, this is single percent sign escaped. - // remove the 2nd percent sign - memmove(pTemplateEnd, pTemplateEnd + 1, &data[len] - pTemplateEnd - 1); - len += _readDataFromCacheOrContent(&data[len - 1], 1) - 1; - ++pTemplateStart; - } - } else if (&data[len - 1] - pTemplateStart + 1 < TEMPLATE_PARAM_NAME_LENGTH + 2) { // closing placeholder not found, check if it's in the remaining file data - memcpy(buf, pTemplateStart + 1, &data[len - 1] - pTemplateStart); - const size_t readFromCacheOrContent = - _readDataFromCacheOrContent(buf + (&data[len - 1] - pTemplateStart), TEMPLATE_PARAM_NAME_LENGTH + 2 - (&data[len - 1] - pTemplateStart + 1)); - if (readFromCacheOrContent) { - pTemplateEnd = (uint8_t *)memchr(buf + (&data[len - 1] - pTemplateStart), TEMPLATE_PLACEHOLDER, readFromCacheOrContent); - if (pTemplateEnd) { - // prepare argument to callback - *pTemplateEnd = 0; - paramName = String(reinterpret_cast(buf)); - // Copy remaining read-ahead data into cache - _cache.insert(_cache.begin(), pTemplateEnd + 1, buf + (&data[len - 1] - pTemplateStart) + readFromCacheOrContent); - pTemplateEnd = &data[len - 1]; - } else // closing placeholder not found in file data, store found percent symbol as is and advance to the next position - { - // but first, store read file data in cache - _cache.insert(_cache.begin(), buf + (&data[len - 1] - pTemplateStart), buf + (&data[len - 1] - pTemplateStart) + readFromCacheOrContent); - ++pTemplateStart; - } - } else // closing placeholder not found in content data, store found percent symbol as is and advance to the next position - ++pTemplateStart; - } else // closing placeholder not found in content data, store found percent symbol as is and advance to the next position - ++pTemplateStart; - if (paramName.length()) { - // call callback and replace with result. - // Everything in range [pTemplateStart, pTemplateEnd] can be safely replaced with parameter value. - // Data after pTemplateEnd may need to be moved. - // The first byte of data after placeholder is located at pTemplateEnd + 1. - // It should be located at pTemplateStart + numBytesCopied (to begin right after inserted parameter value). - const String paramValue(_callback(paramName)); - const char * pvstr = paramValue.c_str(); - const unsigned int pvlen = paramValue.length(); - const size_t numBytesCopied = std::min(pvlen, static_cast(&data[originalLen - 1] - pTemplateStart + 1)); - // make room for param value - // 1. move extra data to cache if parameter value is longer than placeholder AND if there is no room to store - if ((pTemplateEnd + 1 < pTemplateStart + numBytesCopied) && (originalLen - (pTemplateStart + numBytesCopied - pTemplateEnd - 1) < len)) { - _cache.insert(_cache.begin(), &data[originalLen - (pTemplateStart + numBytesCopied - pTemplateEnd - 1)], &data[len]); - //2. parameter value is longer than placeholder text, push the data after placeholder which not saved into cache further to the end - memmove(pTemplateStart + numBytesCopied, pTemplateEnd + 1, &data[originalLen] - pTemplateStart - numBytesCopied); - len = originalLen; // fix issue with truncated data, not sure if it has any side effects - } else if (pTemplateEnd + 1 != pTemplateStart + numBytesCopied) - //2. Either parameter value is shorter than placeholder text OR there is enough free space in buffer to fit. - // Move the entire data after the placeholder - memmove(pTemplateStart + numBytesCopied, pTemplateEnd + 1, &data[len] - pTemplateEnd - 1); - // 3. replace placeholder with actual value - memcpy(pTemplateStart, pvstr, numBytesCopied); - // If result is longer than buffer, copy the remainder into cache (this could happen only if placeholder text itself did not fit entirely in buffer) - if (numBytesCopied < pvlen) { - _cache.insert(_cache.begin(), pvstr + numBytesCopied, pvstr + pvlen); - } else if (pTemplateStart + numBytesCopied < pTemplateEnd + 1) { // result is copied fully; if result is shorter than placeholder text... - // there is some free room, fill it from cache - const size_t roomFreed = pTemplateEnd + 1 - pTemplateStart - numBytesCopied; - const size_t totalFreeRoom = originalLen - len + roomFreed; - len += _readDataFromCacheOrContent(&data[len - roomFreed], totalFreeRoom) - roomFreed; - } else { // result is copied fully; it is longer than placeholder text - const size_t roomTaken = pTemplateStart + numBytesCopied - pTemplateEnd - 1; - len = std::min(len + roomTaken, originalLen); - } - } - } // while(pTemplateStart) - return len; -} - - -/* - * File Response - * */ - -AsyncFileResponse::~AsyncFileResponse() { - if (_content) - _content.close(); -} - -void AsyncFileResponse::_setContentType(const String & path) { -#if HAVE_EXTERN_GET_CONTENT_TYPE_FUNCTION - extern const __FlashStringHelper * getContentType(const String & path); - _contentType = getContentType(path); -#else - if (path.endsWith(F(".html"))) - _contentType = F("text/html"); - else if (path.endsWith(F(".htm"))) - _contentType = F("text/html"); - else if (path.endsWith(F(".css"))) - _contentType = F("text/css"); - else if (path.endsWith(F(".json"))) - _contentType = F("application/json"); - else if (path.endsWith(F(".js"))) - _contentType = F("application/javascript"); - else if (path.endsWith(F(".png"))) - _contentType = F("image/png"); - else if (path.endsWith(F(".gif"))) - _contentType = F("image/gif"); - else if (path.endsWith(F(".jpg"))) - _contentType = F("image/jpeg"); - else if (path.endsWith(F(".ico"))) - _contentType = F("image/x-icon"); - else if (path.endsWith(F(".svg"))) - _contentType = F("image/svg+xml"); - else if (path.endsWith(F(".eot"))) - _contentType = F("font/eot"); - else if (path.endsWith(F(".woff"))) - _contentType = F("font/woff"); - else if (path.endsWith(F(".woff2"))) - _contentType = F("font/woff2"); - else if (path.endsWith(F(".ttf"))) - _contentType = F("font/ttf"); - else if (path.endsWith(F(".xml"))) - _contentType = F("text/xml"); - else if (path.endsWith(F(".pdf"))) - _contentType = F("application/pdf"); - else if (path.endsWith(F(".zip"))) - _contentType = F("application/zip"); - else if (path.endsWith(F(".gz"))) - _contentType = F("application/x-gzip"); - else - _contentType = F("text/plain"); -#endif -} - -AsyncFileResponse::AsyncFileResponse(FS & fs, const String & path, const String & contentType, bool download, AwsTemplateProcessor callback) - : AsyncAbstractResponse(callback) { - _code = 200; - _path = path; - - if (!download && !fs.exists(_path) && fs.exists(_path + F(".gz"))) { - _path = _path + F(".gz"); - addHeader(F("Content-Encoding"), F("gzip")); - _callback = nullptr; // Unable to process zipped templates - _sendContentLength = true; - _chunked = false; - } - - _content = fs.open(_path, fs::FileOpenMode::read); - _contentLength = _content.size(); - - if (contentType.length() == 0) - _setContentType(path); - else - _contentType = contentType; - - int filenameStart = path.lastIndexOf('/') + 1; - char buf[26 + path.length() - filenameStart]; - char * filename = (char *)path.c_str() + filenameStart; - - if (download) { - // set filename and force download - snprintf_P(buf, sizeof(buf), PSTR("attachment; filename=\"%s\""), filename); - } else { - // set filename and force rendering - snprintf_P(buf, sizeof(buf), PSTR("inline; filename=\"%s\""), filename); - } - addHeader(F("Content-Disposition"), buf); -} - -AsyncFileResponse::AsyncFileResponse(File content, const String & path, const String & contentType, bool download, AwsTemplateProcessor callback) - : AsyncAbstractResponse(callback) { - _code = 200; - _path = path; - - if (!download && String(content.name()).endsWith(F(".gz")) && !path.endsWith(F(".gz"))) { - addHeader(F("Content-Encoding"), F("gzip")); - _callback = nullptr; // Unable to process gzipped templates - _sendContentLength = true; - _chunked = false; - } - - _content = content; - _contentLength = _content.size(); - - if (contentType.length() == 0) - _setContentType(path); - else - _contentType = contentType; - - int filenameStart = path.lastIndexOf('/') + 1; - char buf[26 + path.length() - filenameStart]; - char * filename = (char *)path.c_str() + filenameStart; - - if (download) { - snprintf_P(buf, sizeof(buf), PSTR("attachment; filename=\"%s\""), filename); - } else { - snprintf_P(buf, sizeof(buf), PSTR("inline; filename=\"%s\""), filename); - } - addHeader(F("Content-Disposition"), buf); -} - -size_t AsyncFileResponse::_fillBuffer(uint8_t * data, size_t len) { - return _content.read(data, len); -} - -/* - * Stream Response - * */ - -AsyncStreamResponse::AsyncStreamResponse(Stream & stream, const String & contentType, size_t len, AwsTemplateProcessor callback) - : AsyncAbstractResponse(callback) { - _code = 200; - _content = &stream; - _contentLength = len; - _contentType = contentType; -} - -size_t AsyncStreamResponse::_fillBuffer(uint8_t * data, size_t len) { - size_t available = _content->available(); - size_t outLen = (available > len) ? len : available; - size_t i; - for (i = 0; i < outLen; i++) - data[i] = _content->read(); - return outLen; -} - -/* - * Callback Response - * */ - -AsyncCallbackResponse::AsyncCallbackResponse(const String & contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback) - : AsyncAbstractResponse(templateCallback) { - _code = 200; - _content = callback; - _contentLength = len; - if (!len) - _sendContentLength = false; - _contentType = contentType; - _filledLength = 0; -} - -size_t AsyncCallbackResponse::_fillBuffer(uint8_t * data, size_t len) { - size_t ret = _content(data, len, _filledLength); - if (ret != RESPONSE_TRY_AGAIN) { - _filledLength += ret; - } - return ret; -} - -/* - * Chunked Response - * */ - -AsyncChunkedResponse::AsyncChunkedResponse(const String & contentType, AwsResponseFiller callback, AwsTemplateProcessor processorCallback) - : AsyncAbstractResponse(processorCallback) { - _code = 200; - _content = callback; - _contentLength = 0; - _contentType = contentType; - _sendContentLength = false; - _chunked = true; - _filledLength = 0; -} - -size_t AsyncChunkedResponse::_fillBuffer(uint8_t * data, size_t len) { - size_t ret = _content(data, len, _filledLength); - if (ret != RESPONSE_TRY_AGAIN) { - _filledLength += ret; - } - return ret; -} - -/* - * Progmem Response - * */ - -AsyncProgmemResponse::AsyncProgmemResponse(int code, const String & contentType, const uint8_t * content, size_t len, AwsTemplateProcessor callback) - : AsyncAbstractResponse(callback) { - _code = code; - _content = content; - _contentType = contentType; - _contentLength = len; - _readLength = 0; -} - -size_t AsyncProgmemResponse::_fillBuffer(uint8_t * data, size_t len) { - size_t left = _contentLength - _readLength; - if (left > len) { - memcpy_P(data, _content + _readLength, len); - _readLength += len; - return len; - } - memcpy_P(data, _content + _readLength, left); - _readLength += left; - return left; -} - -/* - * Response Stream (You can print/write/printf to it, up to the contentLen bytes) - * */ - -AsyncResponseStream::AsyncResponseStream(const String & contentType, size_t bufferSize) { - _code = 200; - _contentLength = 0; - _contentType = contentType; - _content = std::unique_ptr(new cbuf(bufferSize)); //std::make_unique(bufferSize); -} - -AsyncResponseStream::~AsyncResponseStream() = default; - -size_t AsyncResponseStream::_fillBuffer(uint8_t * buf, size_t maxLen) { - return _content->read((char *)buf, maxLen); -} - -size_t AsyncResponseStream::write(const uint8_t * data, size_t len) { - if (_started()) - return 0; - - if (len > _content->room()) { - size_t needed = len - _content->room(); - _content->resizeAdd(needed); - } - size_t written = _content->write((const char *)data, len); - _contentLength += written; - return written; -} - -size_t AsyncResponseStream::write(uint8_t data) { - return write(&data, 1); -} diff --git a/lib/ESPAsyncWebServer/src/WebServer.cpp b/lib/ESPAsyncWebServer/src/WebServer.cpp deleted file mode 100644 index 5f7309b7e..000000000 --- a/lib/ESPAsyncWebServer/src/WebServer.cpp +++ /dev/null @@ -1,211 +0,0 @@ -/* - Asynchronous WebServer library for Espressif MCUs - - Copyright (c) 2016 Hristo Gochkov. All rights reserved. - This file is part of the esp8266 core 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 -*/ -#include "ESPAsyncWebServer.h" -#include "WebHandlerImpl.h" -#include "AsyncJson.h" - -bool ON_STA_FILTER(AsyncWebServerRequest * request) { - return WiFi.localIP() == request->client()->localIP(); -} - -bool ON_AP_FILTER(AsyncWebServerRequest * request) { - return WiFi.localIP() != request->client()->localIP(); -} - -#ifndef HAVE_FS_FILE_OPEN_MODE -const char * fs::FileOpenMode::read = "r"; -const char * fs::FileOpenMode::write = "w"; -const char * fs::FileOpenMode::append = "a"; -#endif - -AsyncWebServer::AsyncWebServer(uint16_t port) - : _server(port) - , _rewrites(LinkedList([](AsyncWebRewrite * r) { delete r; })) - , _handlers(LinkedList([](AsyncWebHandler * h) { delete h; })) { - _catchAllHandler = new AsyncCallbackWebHandler(); - if (_catchAllHandler == NULL) - return; - _server.onClient( - [](void * s, AsyncClient * c) { - if (c == NULL) - return; - c->setRxTimeout(3); - AsyncWebServerRequest * r = new AsyncWebServerRequest((AsyncWebServer *)s, c); - if (r == NULL) { - c->close(true); - c->free(); - delete c; - } - }, - this); -} - -AsyncWebServer::~AsyncWebServer() { - reset(); - end(); - if (_catchAllHandler) - delete _catchAllHandler; -} - -AsyncWebRewrite & AsyncWebServer::addRewrite(AsyncWebRewrite * rewrite) { - _rewrites.add(rewrite); - return *rewrite; -} - -bool AsyncWebServer::removeRewrite(AsyncWebRewrite * rewrite) { - return _rewrites.remove(rewrite); -} - -AsyncWebRewrite & AsyncWebServer::rewrite(const char * from, const char * to) { - return addRewrite(new AsyncWebRewrite(from, to)); -} - -AsyncWebHandler & AsyncWebServer::addHandler(AsyncWebHandler * handler) { - _handlers.add(handler); - return *handler; -} - -bool AsyncWebServer::removeHandler(AsyncWebHandler * handler) { - return _handlers.remove(handler); -} - -void AsyncWebServer::begin() { - _server.setNoDelay(true); - _server.begin(); -} - -void AsyncWebServer::end() { - _server.end(); -} - -#if ASYNC_TCP_SSL_ENABLED -void AsyncWebServer::onSslFileRequest(AcSSlFileHandler cb, void * arg) { - _server.onSslFileRequest(cb, arg); -} - -void AsyncWebServer::beginSecure(const char * cert, const char * key, const char * password) { - _server.beginSecure(cert, key, password); -} -#endif - -void AsyncWebServer::_handleDisconnect(AsyncWebServerRequest * request) { - delete request; -} - -void AsyncWebServer::_rewriteRequest(AsyncWebServerRequest * request) { - for (const auto & r : _rewrites) { - if (r->match(request)) { - request->_url = r->toUrl(); - request->_addGetParams(r->params()); - } - } -} - -void AsyncWebServer::_attachHandler(AsyncWebServerRequest * request) { - for (const auto & h : _handlers) { - if (h->filter(request) && h->canHandle(request)) { - request->setHandler(h); - return; - } - } - - request->addInterestingHeader(F("ANY")); - request->setHandler(_catchAllHandler); -} - - -AsyncCallbackWebHandler & AsyncWebServer::on(const char * uri, - WebRequestMethodComposite method, - ArRequestHandlerFunction onRequest, - ArUploadHandlerFunction onUpload, - ArBodyHandlerFunction onBody) { - AsyncCallbackWebHandler * handler = new AsyncCallbackWebHandler(); - handler->setUri(uri); - handler->setMethod(method); - handler->onRequest(onRequest); - handler->onUpload(onUpload); - handler->onBody(onBody); - addHandler(handler); - return *handler; -} - -AsyncCallbackWebHandler & -AsyncWebServer::on(const char * uri, WebRequestMethodComposite method, ArRequestHandlerFunction onRequest, ArUploadHandlerFunction onUpload) { - AsyncCallbackWebHandler * handler = new AsyncCallbackWebHandler(); - handler->setUri(uri); - handler->setMethod(method); - handler->onRequest(onRequest); - handler->onUpload(onUpload); - addHandler(handler); - return *handler; -} - -AsyncCallbackWebHandler & AsyncWebServer::on(const char * uri, WebRequestMethodComposite method, ArRequestHandlerFunction onRequest) { - AsyncCallbackWebHandler * handler = new AsyncCallbackWebHandler(); - handler->setUri(uri); - handler->setMethod(method); - handler->onRequest(onRequest); - addHandler(handler); - return *handler; -} - -AsyncCallbackWebHandler & AsyncWebServer::on(const char * uri, ArRequestHandlerFunction onRequest) { - AsyncCallbackWebHandler * handler = new AsyncCallbackWebHandler(); - handler->setUri(uri); - handler->onRequest(onRequest); - addHandler(handler); - return *handler; -} - -// added by proddy for EMS-ESP -void AsyncWebServer::on(const char * uri, ArJsonRequestHandlerFunction onRequest) { - auto * handler = new AsyncCallbackJsonWebHandler(uri, onRequest); - addHandler(handler); -} - -AsyncStaticWebHandler & AsyncWebServer::serveStatic(const char * uri, fs::FS & fs, const char * path, const char * cache_control) { - AsyncStaticWebHandler * handler = new AsyncStaticWebHandler(uri, fs, path, cache_control); - addHandler(handler); - return *handler; -} - -void AsyncWebServer::onNotFound(ArRequestHandlerFunction fn) { - _catchAllHandler->onRequest(fn); -} - -void AsyncWebServer::onFileUpload(ArUploadHandlerFunction fn) { - _catchAllHandler->onUpload(fn); -} - -void AsyncWebServer::onRequestBody(ArBodyHandlerFunction fn) { - _catchAllHandler->onBody(fn); -} - -void AsyncWebServer::reset() { - _rewrites.free(); - _handlers.free(); - - if (_catchAllHandler != NULL) { - _catchAllHandler->onRequest(NULL); - _catchAllHandler->onUpload(NULL); - _catchAllHandler->onBody(NULL); - } -} diff --git a/lib/ESPAsyncWebServer/src/port/SHA1Builder.cpp b/lib/ESPAsyncWebServer/src/port/SHA1Builder.cpp deleted file mode 100644 index b41754dd0..000000000 --- a/lib/ESPAsyncWebServer/src/port/SHA1Builder.cpp +++ /dev/null @@ -1,284 +0,0 @@ -/* - * FIPS-180-1 compliant SHA-1 implementation - * - * Copyright (C) 2006-2015, ARM Limited, All Rights Reserved - * SPDX-License-Identifier: Apache-2.0 - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * This file is part of mbed TLS (https://tls.mbed.org) - * Modified for esp32 by Lucas Saavedra Vaz on 11 Jan 2024 - */ - -#include -#if ESP_IDF_VERSION_MAJOR < 5 - -#include "SHA1Builder.h" - -// 32-bit integer manipulation macros (big endian) - -#ifndef GET_UINT32_BE -#define GET_UINT32_BE(n, b, i) \ - { (n) = ((uint32_t)(b)[(i)] << 24) | ((uint32_t)(b)[(i) + 1] << 16) | ((uint32_t)(b)[(i) + 2] << 8) | ((uint32_t)(b)[(i) + 3]); } -#endif - -#ifndef PUT_UINT32_BE -#define PUT_UINT32_BE(n, b, i) \ - { \ - (b)[(i)] = (uint8_t)((n) >> 24); \ - (b)[(i) + 1] = (uint8_t)((n) >> 16); \ - (b)[(i) + 2] = (uint8_t)((n) >> 8); \ - (b)[(i) + 3] = (uint8_t)((n)); \ - } -#endif - -// Constants - -static const uint8_t sha1_padding[64] = {0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; - -// Private methods - -void SHA1Builder::process(const uint8_t * data) { - uint32_t temp, W[16], A, B, C, D, E; - - GET_UINT32_BE(W[0], data, 0); - GET_UINT32_BE(W[1], data, 4); - GET_UINT32_BE(W[2], data, 8); - GET_UINT32_BE(W[3], data, 12); - GET_UINT32_BE(W[4], data, 16); - GET_UINT32_BE(W[5], data, 20); - GET_UINT32_BE(W[6], data, 24); - GET_UINT32_BE(W[7], data, 28); - GET_UINT32_BE(W[8], data, 32); - GET_UINT32_BE(W[9], data, 36); - GET_UINT32_BE(W[10], data, 40); - GET_UINT32_BE(W[11], data, 44); - GET_UINT32_BE(W[12], data, 48); - GET_UINT32_BE(W[13], data, 52); - GET_UINT32_BE(W[14], data, 56); - GET_UINT32_BE(W[15], data, 60); - -#define sha1_S(x, n) ((x << n) | ((x & 0xFFFFFFFF) >> (32 - n))) - -#define sha1_R(t) (temp = W[(t - 3) & 0x0F] ^ W[(t - 8) & 0x0F] ^ W[(t - 14) & 0x0F] ^ W[t & 0x0F], (W[t & 0x0F] = sha1_S(temp, 1))) - -#define sha1_P(a, b, c, d, e, x) \ - { \ - e += sha1_S(a, 5) + sha1_F(b, c, d) + sha1_K + x; \ - b = sha1_S(b, 30); \ - } - - A = state[0]; - B = state[1]; - C = state[2]; - D = state[3]; - E = state[4]; - -#define sha1_F(x, y, z) (z ^ (x & (y ^ z))) -#define sha1_K 0x5A827999 - - sha1_P(A, B, C, D, E, W[0]); - sha1_P(E, A, B, C, D, W[1]); - sha1_P(D, E, A, B, C, W[2]); - sha1_P(C, D, E, A, B, W[3]); - sha1_P(B, C, D, E, A, W[4]); - sha1_P(A, B, C, D, E, W[5]); - sha1_P(E, A, B, C, D, W[6]); - sha1_P(D, E, A, B, C, W[7]); - sha1_P(C, D, E, A, B, W[8]); - sha1_P(B, C, D, E, A, W[9]); - sha1_P(A, B, C, D, E, W[10]); - sha1_P(E, A, B, C, D, W[11]); - sha1_P(D, E, A, B, C, W[12]); - sha1_P(C, D, E, A, B, W[13]); - sha1_P(B, C, D, E, A, W[14]); - sha1_P(A, B, C, D, E, W[15]); - sha1_P(E, A, B, C, D, sha1_R(16)); - sha1_P(D, E, A, B, C, sha1_R(17)); - sha1_P(C, D, E, A, B, sha1_R(18)); - sha1_P(B, C, D, E, A, sha1_R(19)); - -#undef sha1_K -#undef sha1_F - -#define sha1_F(x, y, z) (x ^ y ^ z) -#define sha1_K 0x6ED9EBA1 - - sha1_P(A, B, C, D, E, sha1_R(20)); - sha1_P(E, A, B, C, D, sha1_R(21)); - sha1_P(D, E, A, B, C, sha1_R(22)); - sha1_P(C, D, E, A, B, sha1_R(23)); - sha1_P(B, C, D, E, A, sha1_R(24)); - sha1_P(A, B, C, D, E, sha1_R(25)); - sha1_P(E, A, B, C, D, sha1_R(26)); - sha1_P(D, E, A, B, C, sha1_R(27)); - sha1_P(C, D, E, A, B, sha1_R(28)); - sha1_P(B, C, D, E, A, sha1_R(29)); - sha1_P(A, B, C, D, E, sha1_R(30)); - sha1_P(E, A, B, C, D, sha1_R(31)); - sha1_P(D, E, A, B, C, sha1_R(32)); - sha1_P(C, D, E, A, B, sha1_R(33)); - sha1_P(B, C, D, E, A, sha1_R(34)); - sha1_P(A, B, C, D, E, sha1_R(35)); - sha1_P(E, A, B, C, D, sha1_R(36)); - sha1_P(D, E, A, B, C, sha1_R(37)); - sha1_P(C, D, E, A, B, sha1_R(38)); - sha1_P(B, C, D, E, A, sha1_R(39)); - -#undef sha1_K -#undef sha1_F - -#define sha1_F(x, y, z) ((x & y) | (z & (x | y))) -#define sha1_K 0x8F1BBCDC - - sha1_P(A, B, C, D, E, sha1_R(40)); - sha1_P(E, A, B, C, D, sha1_R(41)); - sha1_P(D, E, A, B, C, sha1_R(42)); - sha1_P(C, D, E, A, B, sha1_R(43)); - sha1_P(B, C, D, E, A, sha1_R(44)); - sha1_P(A, B, C, D, E, sha1_R(45)); - sha1_P(E, A, B, C, D, sha1_R(46)); - sha1_P(D, E, A, B, C, sha1_R(47)); - sha1_P(C, D, E, A, B, sha1_R(48)); - sha1_P(B, C, D, E, A, sha1_R(49)); - sha1_P(A, B, C, D, E, sha1_R(50)); - sha1_P(E, A, B, C, D, sha1_R(51)); - sha1_P(D, E, A, B, C, sha1_R(52)); - sha1_P(C, D, E, A, B, sha1_R(53)); - sha1_P(B, C, D, E, A, sha1_R(54)); - sha1_P(A, B, C, D, E, sha1_R(55)); - sha1_P(E, A, B, C, D, sha1_R(56)); - sha1_P(D, E, A, B, C, sha1_R(57)); - sha1_P(C, D, E, A, B, sha1_R(58)); - sha1_P(B, C, D, E, A, sha1_R(59)); - -#undef sha1_K -#undef sha1_F - -#define sha1_F(x, y, z) (x ^ y ^ z) -#define sha1_K 0xCA62C1D6 - - sha1_P(A, B, C, D, E, sha1_R(60)); - sha1_P(E, A, B, C, D, sha1_R(61)); - sha1_P(D, E, A, B, C, sha1_R(62)); - sha1_P(C, D, E, A, B, sha1_R(63)); - sha1_P(B, C, D, E, A, sha1_R(64)); - sha1_P(A, B, C, D, E, sha1_R(65)); - sha1_P(E, A, B, C, D, sha1_R(66)); - sha1_P(D, E, A, B, C, sha1_R(67)); - sha1_P(C, D, E, A, B, sha1_R(68)); - sha1_P(B, C, D, E, A, sha1_R(69)); - sha1_P(A, B, C, D, E, sha1_R(70)); - sha1_P(E, A, B, C, D, sha1_R(71)); - sha1_P(D, E, A, B, C, sha1_R(72)); - sha1_P(C, D, E, A, B, sha1_R(73)); - sha1_P(B, C, D, E, A, sha1_R(74)); - sha1_P(A, B, C, D, E, sha1_R(75)); - sha1_P(E, A, B, C, D, sha1_R(76)); - sha1_P(D, E, A, B, C, sha1_R(77)); - sha1_P(C, D, E, A, B, sha1_R(78)); - sha1_P(B, C, D, E, A, sha1_R(79)); - -#undef sha1_K -#undef sha1_F - - state[0] += A; - state[1] += B; - state[2] += C; - state[3] += D; - state[4] += E; -} - -// Public methods - -void SHA1Builder::begin(void) { - total[0] = 0; - total[1] = 0; - - state[0] = 0x67452301; - state[1] = 0xEFCDAB89; - state[2] = 0x98BADCFE; - state[3] = 0x10325476; - state[4] = 0xC3D2E1F0; - - memset(buffer, 0x00, sizeof(buffer)); - memset(hash, 0x00, sizeof(hash)); -} - -void SHA1Builder::add(const uint8_t * data, size_t len) { - size_t fill; - uint32_t left; - - if (len == 0) { - return; - } - - left = total[0] & 0x3F; - fill = 64 - left; - - total[0] += (uint32_t)len; - total[0] &= 0xFFFFFFFF; - - if (total[0] < (uint32_t)len) { - total[1]++; - } - - if (left && len >= fill) { - memcpy((void *)(buffer + left), data, fill); - process(buffer); - data += fill; - len -= fill; - left = 0; - } - - while (len >= 64) { - process(data); - data += 64; - len -= 64; - } - - if (len > 0) { - memcpy((void *)(buffer + left), data, len); - } -} - -void SHA1Builder::calculate(void) { - uint32_t last, padn; - uint32_t high, low; - uint8_t msglen[8]; - - high = (total[0] >> 29) | (total[1] << 3); - low = (total[0] << 3); - - PUT_UINT32_BE(high, msglen, 0); - PUT_UINT32_BE(low, msglen, 4); - - last = total[0] & 0x3F; - padn = (last < 56) ? (56 - last) : (120 - last); - - add((uint8_t *)sha1_padding, padn); - add(msglen, 8); - - PUT_UINT32_BE(state[0], hash, 0); - PUT_UINT32_BE(state[1], hash, 4); - PUT_UINT32_BE(state[2], hash, 8); - PUT_UINT32_BE(state[3], hash, 12); - PUT_UINT32_BE(state[4], hash, 16); -} - -void SHA1Builder::getBytes(uint8_t * output) { - memcpy(output, hash, SHA1_HASH_SIZE); -} - -#endif // ESP_IDF_VERSION_MAJOR < 5 \ No newline at end of file diff --git a/lib/ESPAsyncWebServer/src/port/SHA1Builder.h b/lib/ESPAsyncWebServer/src/port/SHA1Builder.h deleted file mode 100644 index 5049cde39..000000000 --- a/lib/ESPAsyncWebServer/src/port/SHA1Builder.h +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright 2024 Espressif Systems (Shanghai) PTE LTD -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef SHA1Builder_h -#define SHA1Builder_h - -#include -#include - -#define SHA1_HASH_SIZE 20 - -class SHA1Builder { - private: - uint32_t total[2]; /* number of bytes processed */ - uint32_t state[5]; /* intermediate digest state */ - unsigned char buffer[64]; /* data block being processed */ - uint8_t hash[SHA1_HASH_SIZE]; /* SHA-1 result */ - - void process(const uint8_t * data); - - public: - void begin(); - void add(const uint8_t * data, size_t len); - void calculate(); - void getBytes(uint8_t * output); -}; - -#endif // SHA1Builder_h \ No newline at end of file diff --git a/lib/OneWire/library.properties b/lib/OneWire/library.properties deleted file mode 100644 index 89313ddaa..000000000 --- a/lib/OneWire/library.properties +++ /dev/null @@ -1,10 +0,0 @@ -name=OneWire -version=2.3.3 -author=Jim Studt, Tom Pollard, Robin James, Glenn Trewitt, Jason Dangel, Guillermo Lovato, Paul Stoffregen, Scott Roberts, Bertrik Sikken, Mark Tillotson, Ken Butcher, Roger Clark, Love Nystrom -maintainer=Paul Stoffregen -sentence=Access 1-wire temperature sensors, memory and other chips. -paragraph= Mod of Paul Stoffregen code to support ESP32 -category=Communication -url=http://www.pjrc.com/teensy/td_libs_OneWire.html -architectures=esp8266,esp32 - diff --git a/lib/eModbus/keywords.txt b/lib/eModbus/keywords.txt deleted file mode 100644 index fbbc9db52..000000000 --- a/lib/eModbus/keywords.txt +++ /dev/null @@ -1,257 +0,0 @@ -####################################### -# Syntax Coloring Map For the current project. -# This file was generated by doxygen2keywords.xsl. -####################################### - -####################################### -# Classes and structs (KEYWORD1) -####################################### - -ModbusServerTCP::ClientData KEYWORD1 -CoilData KEYWORD1 -Modbus::FCT KEYWORD1 -ModbusBridge KEYWORD1 -ModbusClient KEYWORD1 -ModbusClientTCP KEYWORD1 -ModbusClientRTU KEYWORD1 -ModbusClientTCPasync KEYWORD1 -ModbusError KEYWORD1 -ModbusMessage KEYWORD1 -ModbusServer KEYWORD1 -ModbusServerTCP KEYWORD1 -ModbusServerRTU KEYWORD1 -ModbusServerTCPasync KEYWORD1 -RTUutils KEYWORD1 - -####################################### -# Methods (KEYWORD2) -####################################### - -ClientData KEYWORD2 -~ClientData KEYWORD2 -CoilData KEYWORD2 -~CoilData KEYWORD2 -coils KEYWORD2 -coilsSetON KEYWORD2 -coilsSetOFF KEYWORD2 -FCT KEYWORD2 -getType KEYWORD2 -redefineType KEYWORD2 -ModbusBridge KEYWORD2 -attachServer KEYWORD2 -addFunctionCode KEYWORD2 -denyFunctionCode KEYWORD2 -bridgeWorker KEYWORD2 -bridgeDenyWorker KEYWORD2 -onDataHandler KEYWORD2 -onErrorHandler KEYWORD2 -onResponseHandler KEYWORD2 -getMessageCount KEYWORD2 -getErrorCount KEYWORD2 -resetCounts KEYWORD2 -addRequest KEYWORD2 -syncRequest KEYWORD2 -buildErrorMsg KEYWORD2 -addRequest KEYWORD2 -ModbusClient KEYWORD2 -waitSync KEYWORD2 -ModbusClientTCPasync KEYWORD2 -setTimeout KEYWORD2 -setIdleTimeout KEYWORD2 -setMaxInflightRequests KEYWORD2 -addToQueue KEYWORD2 -ModbusError KEYWORD2 -getText KEYWORD2 -ModbusMessage KEYWORD2 -data KEYWORD2 -size KEYWORD2 -push_back KEYWORD2 -clear KEYWORD2 -resize KEYWORD2 -begin KEYWORD2 -end KEYWORD2 -append KEYWORD2 -getServerID KEYWORD2 -getFunctionCode KEYWORD2 -getError KEYWORD2 -setFunctionCode KEYWORD2 -add KEYWORD2 -get KEYWORD2 -setMessage KEYWORD2 -setError KEYWORD2 -determineFloatOrder KEYWORD2 -determineDoubleOrder KEYWORD2 -swapFloat KEYWORD2 -swapDouble KEYWORD2 -getOne KEYWORD2 -registerWorker KEYWORD2 -getWorker KEYWORD2 -unregisterWorker KEYWORD2 -isServerFor KEYWORD2 -getMessageCount KEYWORD2 -getErrorCount KEYWORD2 -resetCounts KEYWORD2 -localRequest KEYWORD2 -listServer KEYWORD2 -ModbusServer KEYWORD2 -ModbusServerTCP KEYWORD2 -activeClients KEYWORD2 -start KEYWORD2 -stop KEYWORD2 -clientAvailable KEYWORD2 -ModbusServerTCPasync KEYWORD2 -isRunning KEYWORD2 -calcCRC KEYWORD2 -validCRC KEYWORD2 -addCRC KEYWORD2 -calculateInterval KEYWORD2 -prepareHardwareSerial KEYWORD2 -RTUutils KEYWORD2 -ServerData KEYWORD2 -NIL_RESPONSE KEYWORD2 -ECHO_RESPONSE KEYWORD2 - -####################################### -# Constants (LITERAL1) -####################################### - -DISCONNECTED LITERAL1 -CONNECTING LITERAL1 -CONNECTED LITERAL1 -ANY_FUNCTION_CODE LITERAL1 -READ_COIL LITERAL1 -READ_DISCR_INPUT LITERAL1 -READ_HOLD_REGISTER LITERAL1 -READ_INPUT_REGISTER LITERAL1 -WRITE_COIL LITERAL1 -WRITE_HOLD_REGISTER LITERAL1 -READ_EXCEPTION_SERIAL LITERAL1 -DIAGNOSTICS_SERIAL LITERAL1 -READ_COMM_CNT_SERIAL LITERAL1 -READ_COMM_LOG_SERIAL LITERAL1 -WRITE_MULT_COILS LITERAL1 -WRITE_MULT_REGISTERS LITERAL1 -REPORT_SERVER_ID_SERIAL LITERAL1 -READ_FILE_RECORD LITERAL1 -WRITE_FILE_RECORD LITERAL1 -MASK_WRITE_REGISTER LITERAL1 -R_W_MULT_REGISTERS LITERAL1 -READ_FIFO_QUEUE LITERAL1 -ENCAPSULATED_INTERFACE LITERAL1 -USER_DEFINED_41 LITERAL1 -USER_DEFINED_42 LITERAL1 -USER_DEFINED_43 LITERAL1 -USER_DEFINED_44 LITERAL1 -USER_DEFINED_45 LITERAL1 -USER_DEFINED_46 LITERAL1 -USER_DEFINED_47 LITERAL1 -USER_DEFINED_48 LITERAL1 -USER_DEFINED_64 LITERAL1 -USER_DEFINED_65 LITERAL1 -USER_DEFINED_66 LITERAL1 -USER_DEFINED_67 LITERAL1 -USER_DEFINED_68 LITERAL1 -USER_DEFINED_69 LITERAL1 -USER_DEFINED_6A LITERAL1 -USER_DEFINED_6B LITERAL1 -USER_DEFINED_6C LITERAL1 -USER_DEFINED_6D LITERAL1 -USER_DEFINED_6E LITERAL1 -SUCCESS LITERAL1 -ILLEGAL_FUNCTION LITERAL1 -ILLEGAL_DATA_ADDRESS LITERAL1 -ILLEGAL_DATA_VALUE LITERAL1 -SERVER_DEVICE_FAILURE LITERAL1 -ACKNOWLEDGE LITERAL1 -SERVER_DEVICE_BUSY LITERAL1 -NEGATIVE_ACKNOWLEDGE LITERAL1 -MEMORY_PARITY_ERROR LITERAL1 -GATEWAY_PATH_UNAVAIL LITERAL1 -GATEWAY_TARGET_NO_RESP LITERAL1 -TIMEOUT LITERAL1 -INVALID_SERVER LITERAL1 -CRC_ERROR LITERAL1 -FC_MISMATCH LITERAL1 -SERVER_ID_MISMATCH LITERAL1 -PACKET_LENGTH_ERROR LITERAL1 -PARAMETER_COUNT_ERROR LITERAL1 -PARAMETER_LIMIT_ERROR LITERAL1 -REQUEST_QUEUE_FULL LITERAL1 -ILLEGAL_IP_OR_PORT LITERAL1 -IP_CONNECTION_FAILED LITERAL1 -TCP_HEAD_MISMATCH LITERAL1 -EMPTY_MESSAGE LITERAL1 -ASCII_FRAME_ERR LITERAL1 -ASCII_CRC_ERR LITERAL1 -ASCII_INVALID_CHAR LITERAL1 -BROADCAST_ERROR LITERAL1 -UNDEFINED_ERROR LITERAL1 -FC01_TYPE LITERAL1 -FC07_TYPE LITERAL1 -FC0F_TYPE LITERAL1 -FC10_TYPE LITERAL1 -FC16_TYPE LITERAL1 -FC18_TYPE LITERAL1 -FCGENERIC LITERAL1 -FCUSER LITERAL1 -FCILLEGAL LITERAL1 -PrintOut LITERAL1 -LOG_LEVEL LITERAL1 -LOCAL_LOG_LEVEL LITERAL1 -LOG_LEVEL_NONE LITERAL1 -LOG_LEVEL_CRITICAL LITERAL1 -LOG_LEVEL_ERROR LITERAL1 -LOG_LEVEL_WARNING LITERAL1 -LOG_LEVEL_INFO LITERAL1 -LOG_LEVEL_DEBUG LITERAL1 -LOG_LEVEL_VERBOSE LITERAL1 -LL_RED LITERAL1 -LL_GREEN LITERAL1 -LL_YELLOW LITERAL1 -LL_BLUE LITERAL1 -LL_MAGENTA LITERAL1 -LL_CYAN LITERAL1 -LL_NORM LITERAL1 -LOG_HEADER LITERAL1 -LOG_LINE_C LITERAL1 -LOG_LINE_E LITERAL1 -LOG_LINE_T LITERAL1 -LOG_RAW_C LITERAL1 -LOG_RAW_E LITERAL1 -LOG_RAW_T LITERAL1 -HEX_DUMP_T LITERAL1 -LOG_N LITERAL1 -LOGRAW_N LITERAL1 -HEXDUMP_N LITERAL1 -LOG_C LITERAL1 -LOGRAW_C LITERAL1 -HEXDUMP_C LITERAL1 -LOG_E LITERAL1 -LOGRAW_E LITERAL1 -HEXDUMP_E LITERAL1 -LOG_W LITERAL1 -LOGRAW_W LITERAL1 -HEXDUMP_W LITERAL1 -LOG_I LITERAL1 -LOGRAW_I LITERAL1 -HEXDUMP_I LITERAL1 -LOG_D LITERAL1 -LOGRAW_D LITERAL1 -HEXDUMP_D LITERAL1 -LOG_V LITERAL1 -LOGRAW_V LITERAL1 -HEXDUMP_V LITERAL1 -LOCAL_LOG_LEVEL LITERAL1 -TCP_SERVER LITERAL1 -RTU_SERVER LITERAL1 -SERVER_END LITERAL1 -LOCAL_LOG_LEVEL LITERAL1 -DEFAULTTIMEOUT LITERAL1 -DEFAULTIDLETIME LITERAL1 -LOCAL_LOG_LEVEL LITERAL1 -SERVER_END LITERAL1 -SWAP_BYTES LITERAL1 -SWAP_REGISTERS LITERAL1 -SWAP_WORDS LITERAL1 -SWAP_NIBBLES LITERAL1 -LOCK_GUARD LITERAL1 diff --git a/lib/eModbus/library.properties b/lib/eModbus/library.properties deleted file mode 100644 index 4dab600ac..000000000 --- a/lib/eModbus/library.properties +++ /dev/null @@ -1,9 +0,0 @@ -name=eModbus -version=1.7.2 -author=bertmelis,Miq1 -maintainer=Miq1 -sentence=eModbus provides Modbus RTU, ASCII and TCP functions for ESP32. -paragraph=This library is non-blocking for the program using it. Modbus requests and responses will be returned to user-supplied callback functions. All Modbus function codes are supported implicitly, the codes specified by the Modbus specs are parameter-checked. -category=Communication -url=https://github.com/eModbus/eModbus -architectures=esp32,FreeRTOS diff --git a/lib/espMqttClient/README.md b/lib/espMqttClient/README.md index c897d3d35..f92652ed0 100644 --- a/lib/espMqttClient/README.md +++ b/lib/espMqttClient/README.md @@ -12,6 +12,7 @@ with additional changes to support EMS-ESP such as compiling with Tasmota and no ``` src/espMqttClient.cpp src/Transport/ClientSecureSync.h + src/Config.h ``` # License diff --git a/lib/framework/Features.h b/lib/framework/Features.h deleted file mode 100644 index d5cccc997..000000000 --- a/lib/framework/Features.h +++ /dev/null @@ -1,29 +0,0 @@ -#ifndef Features_h -#define Features_h - -// project feature on by default -#ifndef FT_PROJECT -#define FT_PROJECT 1 -#endif - -// security feature on by default -#ifndef FT_SECURITY -#define FT_SECURITY 1 -#endif - -// mqtt feature on by default -#ifndef FT_MQTT -#define FT_MQTT 1 -#endif - -// ntp feature on by default -#ifndef FT_NTP -#define FT_NTP 1 -#endif - -// upload firmware/file feature on by default -#ifndef FT_UPLOAD_FIRMWARE -#define FT_UPLOAD_FIRMWARE 1 -#endif - -#endif diff --git a/lib/semver/Semver200_parser.cpp b/lib/semver/Semver200_parser.cpp index 77bcbd5a7..83385208c 100644 --- a/lib/semver/Semver200_parser.cpp +++ b/lib/semver/Semver200_parser.cpp @@ -26,13 +26,6 @@ SOFTWARE. #include #include "semver200.h" -#include "../../src/emsesp_stub.hpp" // for logging - -#ifdef _MSC_VER -// disable symbol name too long warning -#pragma warning(disable : 4503) -#endif - using namespace std; namespace version { @@ -57,7 +50,7 @@ inline Transition mkx(const char c, Parser_state p, State_transition_hook pth) { } inline void Parse_error(const std::string & s) { - emsesp::EMSESP::logger().err("parse error: %s", s.c_str()); + // emsesp::EMSESP::logger().err("parse error: %s", s.c_str()); } /// Advance parser state machine by a single step. diff --git a/lib/uuid-syslog/src/syslog.cpp b/lib/uuid-syslog/src/syslog.cpp index 313da45b6..9c7a7c0ab 100644 --- a/lib/uuid-syslog/src/syslog.cpp +++ b/lib/uuid-syslog/src/syslog.cpp @@ -18,8 +18,6 @@ #include "uuid/syslog.h" -#include "../../../src/emsesp.h" - #ifndef UUID_SYSLOG_HAVE_GETTIMEOFDAY #if defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32) // time() does not return UTC on the ESP8266: https://github.com/esp8266/Arduino/issues/4637 @@ -233,8 +231,8 @@ SyslogService::QueuedLogMessage::QueuedLogMessage(unsigned long id, std::shared_ : id_(id) , content_(std::move(content)) { // Added for EMS-ESP - // check for Ethernet too. This assumes the network has already started. - if (time_good_ || emsesp::EMSESP::system_.network_connected()) { + // if (time_good_ || emsesp::EMSESP::system_.network_connected()) { + if (time_good_) { #if UUID_SYSLOG_HAVE_GETTIMEOFDAY if (gettimeofday(&time_, nullptr) != 0) { time_.tv_sec = (time_t)-1; @@ -364,9 +362,9 @@ bool SyslogService::can_transmit() { } #endif - if (!emsesp::EMSESP::system_.network_connected()) { - return false; - } + // if (!emsesp::EMSESP::system_.network_connected()) { + // return false; + // } const uint64_t now = uuid::get_uptime_ms(); uint64_t message_delay = UUID_SYSLOG_UDP_BASE_MESSAGE_DELAY; diff --git a/lib/ArduinoJson/ArduinoJson.h b/lib_standalone/ArduinoJson.h similarity index 90% rename from lib/ArduinoJson/ArduinoJson.h rename to lib_standalone/ArduinoJson.h index 89d63b3e1..644d19c6c 100644 --- a/lib/ArduinoJson/ArduinoJson.h +++ b/lib_standalone/ArduinoJson.h @@ -239,11 +239,11 @@ #define ARDUINOJSON_BIN2ALPHA_1111() P #define ARDUINOJSON_BIN2ALPHA_(A, B, C, D) ARDUINOJSON_BIN2ALPHA_##A##B##C##D() #define ARDUINOJSON_BIN2ALPHA(A, B, C, D) ARDUINOJSON_BIN2ALPHA_(A, B, C, D) -#define ARDUINOJSON_VERSION "7.2.1" +#define ARDUINOJSON_VERSION "7.3.0" #define ARDUINOJSON_VERSION_MAJOR 7 -#define ARDUINOJSON_VERSION_MINOR 2 -#define ARDUINOJSON_VERSION_REVISION 1 -#define ARDUINOJSON_VERSION_MACRO V721 +#define ARDUINOJSON_VERSION_MINOR 3 +#define ARDUINOJSON_VERSION_REVISION 0 +#define ARDUINOJSON_VERSION_MACRO V730 #ifndef ARDUINOJSON_VERSION_NAMESPACE # define ARDUINOJSON_VERSION_NAMESPACE \ ARDUINOJSON_CONCAT5( \ @@ -426,6 +426,20 @@ struct conditional { template using conditional_t = typename conditional::type; +template +struct decay { + using type = T; +}; +template +struct decay : decay {}; +template +struct decay : decay {}; +template +struct decay : decay {}; +template +struct decay : decay {}; +template +using decay_t = typename decay::type; template struct enable_if {}; template @@ -928,35 +942,52 @@ ARDUINOJSON_END_PRIVATE_NAMESPACE # define ARDUINOJSON_NO_SANITIZE(check) #endif ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE +template +struct IsStringLiteral : false_type {}; +template +struct IsStringLiteral : true_type {}; template struct StringAdapter; template struct SizedStringAdapter; template -typename StringAdapter::AdaptedString adaptString(const TString& s) { - return StringAdapter::adapt(s); +using StringAdapterFor = + StringAdapter::value, TString, + remove_cv_t>>>; +template +using AdaptedString = typename StringAdapterFor::AdaptedString; +template +AdaptedString adaptString(TString&& s) { + return StringAdapterFor::adapt(detail::forward(s)); } -template -typename StringAdapter::AdaptedString adaptString(TChar* p) { +template ::value, int> = 0> +AdaptedString adaptString(TChar* p) { return StringAdapter::adapt(p); } template -typename SizedStringAdapter::AdaptedString adaptString(TChar* p, - size_t n) { +AdaptedString adaptString(TChar* p, size_t n) { return SizedStringAdapter::adapt(p, n); } template struct IsChar : integral_constant::value && sizeof(T) == 1> {}; -class ZeroTerminatedRamString { +class RamString { public: - static const size_t typeSortKey = 3; - ZeroTerminatedRamString(const char* str) : str_(str) {} + static const size_t typeSortKey = 2; +#if ARDUINOJSON_SIZEOF_POINTER <= 2 + static constexpr size_t sizeMask = size_t(-1) >> 1; +#else + static constexpr size_t sizeMask = size_t(-1); +#endif + RamString(const char* str, size_t sz, bool isStatic = false) + : str_(str), size_(sz & sizeMask), static_(isStatic) { + ARDUINOJSON_ASSERT(size_ == sz); + } bool isNull() const { return !str_; } - FORCE_INLINE size_t size() const { - return str_ ? ::strlen(str_) : 0; + size_t size() const { + return size_; } char operator[](size_t i) const { ARDUINOJSON_ASSERT(str_ != 0); @@ -966,144 +997,49 @@ class ZeroTerminatedRamString { const char* data() const { return str_; } - bool isLinked() const { - return false; + bool isStatic() const { + return static_; } protected: const char* str_; +#if ARDUINOJSON_SIZEOF_POINTER <= 2 + size_t size_ : sizeof(size_t) * 8 - 1; + bool static_ : 1; +#else + size_t size_; + bool static_; +#endif }; template struct StringAdapter::value>> { - using AdaptedString = ZeroTerminatedRamString; + using AdaptedString = RamString; static AdaptedString adapt(const TChar* p) { - return AdaptedString(reinterpret_cast(p)); + auto str = reinterpret_cast(p); + return AdaptedString(str, str ? ::strlen(str) : 0); + } +}; +template +struct StringAdapter { + using AdaptedString = RamString; + static AdaptedString adapt(const char (&p)[N]) { + return RamString(p, N - 1, true); } }; template struct StringAdapter::value>> { - using AdaptedString = ZeroTerminatedRamString; + using AdaptedString = RamString; static AdaptedString adapt(const TChar* p) { - return AdaptedString(reinterpret_cast(p)); + auto str = reinterpret_cast(p); + return AdaptedString(str, str ? ::strlen(str) : 0); } }; -class StaticStringAdapter : public ZeroTerminatedRamString { - public: - StaticStringAdapter(const char* str) : ZeroTerminatedRamString(str) {} - bool isLinked() const { - return true; - } -}; -template <> -struct StringAdapter { - using AdaptedString = StaticStringAdapter; - static AdaptedString adapt(const char* p) { - return AdaptedString(p); - } -}; -class SizedRamString { - public: - static const size_t typeSortKey = 2; - SizedRamString(const char* str, size_t sz) : str_(str), size_(sz) {} - bool isNull() const { - return !str_; - } - size_t size() const { - return size_; - } - char operator[](size_t i) const { - ARDUINOJSON_ASSERT(str_ != 0); - ARDUINOJSON_ASSERT(i <= size()); - return str_[i]; - } - const char* data() const { - return str_; - } - bool isLinked() const { - return false; - } - protected: - const char* str_; - size_t size_; -}; template struct SizedStringAdapter::value>> { - using AdaptedString = SizedRamString; + using AdaptedString = RamString; static AdaptedString adapt(const TChar* p, size_t n) { return AdaptedString(reinterpret_cast(p), n); } }; -ARDUINOJSON_END_PRIVATE_NAMESPACE -#if ARDUINOJSON_ENABLE_STD_STREAM -#include -#endif -ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE -class JsonString { - public: - enum Ownership { Copied, Linked }; - JsonString() : data_(0), size_(0), ownership_(Linked) {} - JsonString(const char* data, Ownership ownership = Linked) - : data_(data), size_(data ? ::strlen(data) : 0), ownership_(ownership) {} - JsonString(const char* data, size_t sz, Ownership ownership = Linked) - : data_(data), size_(sz), ownership_(ownership) {} - const char* c_str() const { - return data_; - } - bool isNull() const { - return !data_; - } - bool isLinked() const { - return ownership_ == Linked; - } - size_t size() const { - return size_; - } - explicit operator bool() const { - return data_ != 0; - } - friend bool operator==(JsonString lhs, JsonString rhs) { - if (lhs.size_ != rhs.size_) - return false; - if (lhs.data_ == rhs.data_) - return true; - if (!lhs.data_) - return false; - if (!rhs.data_) - return false; - return memcmp(lhs.data_, rhs.data_, lhs.size_) == 0; - } - friend bool operator!=(JsonString lhs, JsonString rhs) { - return !(lhs == rhs); - } -#if ARDUINOJSON_ENABLE_STD_STREAM - friend std::ostream& operator<<(std::ostream& lhs, const JsonString& rhs) { - lhs.write(rhs.c_str(), static_cast(rhs.size())); - return lhs; - } -#endif - private: - const char* data_; - size_t size_; - Ownership ownership_; -}; -ARDUINOJSON_END_PUBLIC_NAMESPACE -ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE -class JsonStringAdapter : public SizedRamString { - public: - JsonStringAdapter(const JsonString& s) - : SizedRamString(s.c_str(), s.size()), linked_(s.isLinked()) {} - bool isLinked() const { - return linked_; - } - private: - bool linked_; -}; -template <> -struct StringAdapter { - using AdaptedString = JsonStringAdapter; - static AdaptedString adapt(const JsonString& s) { - return AdaptedString(s); - } -}; namespace string_traits_impl { template struct has_cstr : false_type {}; @@ -1142,7 +1078,7 @@ struct StringAdapter< T, enable_if_t<(string_traits::has_cstr || string_traits::has_data) && (string_traits::has_length || string_traits::has_size)>> { - using AdaptedString = SizedRamString; + using AdaptedString = RamString; static AdaptedString adapt(const T& s) { return AdaptedString(get_data(s), get_size(s)); } @@ -1307,7 +1243,7 @@ class FlashString { size_t size() const { return size_; } - friend bool stringEquals(FlashString a, SizedRamString b) { + friend bool stringEquals(FlashString a, RamString b) { ARDUINOJSON_ASSERT(a.typeSortKey < b.typeSortKey); ARDUINOJSON_ASSERT(!a.isNull()); ARDUINOJSON_ASSERT(!b.isNull()); @@ -1315,7 +1251,7 @@ class FlashString { return false; return ::memcmp_P(b.data(), a.str_, a.size_) == 0; } - friend int stringCompare(FlashString a, SizedRamString b) { + friend int stringCompare(FlashString a, RamString b) { ARDUINOJSON_ASSERT(a.typeSortKey < b.typeSortKey); ARDUINOJSON_ASSERT(!a.isNull()); ARDUINOJSON_ASSERT(!b.isNull()); @@ -1333,7 +1269,7 @@ class FlashString { ARDUINOJSON_ASSERT(s.size() <= n); ::memcpy_P(p, s.str_, n); } - bool isLinked() const { + bool isStatic() const { return false; } private: @@ -1863,6 +1799,70 @@ ARDUINOJSON_END_PRIVATE_NAMESPACE #elif defined(__GNUC__) # pragma GCC diagnostic pop #endif +#if ARDUINOJSON_ENABLE_STD_STREAM +#include +#endif +ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE +class JsonString { + friend struct detail::StringAdapter; + public: + JsonString() : str_(nullptr, 0, true) {} + JsonString(const char* data, bool isStatic = false) + : str_(data, data ? ::strlen(data) : 0, isStatic) {} + template ::value && + !detail::is_same::value, + int> = 0> + JsonString(const char* data, TSize sz, bool isStatic = false) + : str_(data, size_t(sz), isStatic) {} + const char* c_str() const { + return str_.data(); + } + bool isNull() const { + return str_.isNull(); + } + bool isStatic() const { + return str_.isStatic(); + } + size_t size() const { + return str_.size(); + } + explicit operator bool() const { + return str_.data() != 0; + } + friend bool operator==(JsonString lhs, JsonString rhs) { + if (lhs.size() != rhs.size()) + return false; + if (lhs.c_str() == rhs.c_str()) + return true; + if (!lhs.c_str()) + return false; + if (!rhs.c_str()) + return false; + return memcmp(lhs.c_str(), rhs.c_str(), lhs.size()) == 0; + } + friend bool operator!=(JsonString lhs, JsonString rhs) { + return !(lhs == rhs); + } +#if ARDUINOJSON_ENABLE_STD_STREAM + friend std::ostream& operator<<(std::ostream& lhs, const JsonString& rhs) { + lhs.write(rhs.c_str(), static_cast(rhs.size())); + return lhs; + } +#endif + private: + detail::RamString str_; +}; +namespace detail { +template <> +struct StringAdapter { + using AdaptedString = RamString; + static const AdaptedString& adapt(const JsonString& s) { + return s.str_; + } +}; +} // namespace detail +ARDUINOJSON_END_PUBLIC_NAMESPACE ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE class VariantData; class ResourceManager; @@ -1951,9 +1951,9 @@ class ArrayData : public CollectionData { return array->addElement(resources); } template - bool addValue(T&& value, ResourceManager* resources); + bool addValue(const T& value, ResourceManager* resources); template - static bool addValue(ArrayData* array, T&& value, + static bool addValue(ArrayData* array, const T& value, ResourceManager* resources) { if (!array) return false; @@ -2141,11 +2141,10 @@ class VariantData { case VariantType::Object: return visit.visit(content_.asObject); case VariantType::LinkedString: - return visit.visit(JsonString(content_.asLinkedString)); + return visit.visit(JsonString(content_.asLinkedString, true)); case VariantType::OwnedString: return visit.visit(JsonString(content_.asOwnedString->data, - content_.asOwnedString->length, - JsonString::Copied)); + content_.asOwnedString->length)); case VariantType::RawString: return visit.visit(RawString(content_.asOwnedString->data, content_.asOwnedString->length)); @@ -2184,13 +2183,12 @@ class VariantData { return var->addElement(resources); } template - bool addValue(T&& value, ResourceManager* resources) { + bool addValue(const T& value, ResourceManager* resources) { auto array = isNull() ? &toArray() : asArray(); - return detail::ArrayData::addValue(array, detail::forward(value), - resources); + return detail::ArrayData::addValue(array, value, resources); } template - static bool addValue(VariantData* var, T&& value, + static bool addValue(VariantData* var, const T& value, ResourceManager* resources) { if (!var) return false; @@ -2316,7 +2314,7 @@ class VariantData { switch (type_) { case VariantType::RawString: return JsonString(content_.asOwnedString->data, - content_.asOwnedString->length, JsonString::Copied); + content_.asOwnedString->length); default: return JsonString(); } @@ -2324,10 +2322,10 @@ class VariantData { JsonString asString() const { switch (type_) { case VariantType::LinkedString: - return JsonString(content_.asLinkedString, JsonString::Linked); + return JsonString(content_.asLinkedString, true); case VariantType::OwnedString: return JsonString(content_.asOwnedString->data, - content_.asOwnedString->length, JsonString::Copied); + content_.asOwnedString->length); default: return JsonString(); } @@ -2654,7 +2652,7 @@ class ResourceManager { template struct IsString : false_type {}; template -struct IsString::AdaptedString>> +struct IsString::AdaptedString>> : true_type {}; ARDUINOJSON_END_PRIVATE_NAMESPACE ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE @@ -2798,9 +2796,9 @@ CompareResult compare(JsonVariantConst lhs, struct VariantOperatorTag {}; template struct VariantOperators : VariantOperatorTag { - template - friend enable_if_t::value && !is_array::value, T> operator|( - const TVariant& variant, const T& defaultValue) { + template ::value && !is_array::value, int> = 0> + friend T operator|(const TVariant& variant, const T& defaultValue) { if (variant.template is()) return variant.template as(); else @@ -2815,112 +2813,112 @@ struct VariantOperators : VariantOperatorTag { } template friend enable_if_t::value, JsonVariantConst> operator|( - const TVariant& variant, T defaultValue) { + const TVariant& variant, const T& defaultValue) { if (variant) return variant; else return defaultValue; } template - friend bool operator==(T* lhs, TVariant rhs) { + friend bool operator==(T* lhs, const TVariant& rhs) { return compare(rhs, lhs) == COMPARE_RESULT_EQUAL; } template - friend bool operator==(const T& lhs, TVariant rhs) { + friend bool operator==(const T& lhs, const TVariant& rhs) { return compare(rhs, lhs) == COMPARE_RESULT_EQUAL; } template - friend bool operator==(TVariant lhs, T* rhs) { + friend bool operator==(const TVariant& lhs, T* rhs) { return compare(lhs, rhs) == COMPARE_RESULT_EQUAL; } - template - friend enable_if_t::value, bool> - operator==(TVariant lhs, const T& rhs) { + template ::value, int> = 0> + friend bool operator==(const TVariant& lhs, const T& rhs) { return compare(lhs, rhs) == COMPARE_RESULT_EQUAL; } template - friend bool operator!=(T* lhs, TVariant rhs) { + friend bool operator!=(T* lhs, const TVariant& rhs) { return compare(rhs, lhs) != COMPARE_RESULT_EQUAL; } template - friend bool operator!=(const T& lhs, TVariant rhs) { + friend bool operator!=(const T& lhs, const TVariant& rhs) { return compare(rhs, lhs) != COMPARE_RESULT_EQUAL; } template - friend bool operator!=(TVariant lhs, T* rhs) { + friend bool operator!=(const TVariant& lhs, T* rhs) { return compare(lhs, rhs) != COMPARE_RESULT_EQUAL; } - template - friend enable_if_t::value, bool> - operator!=(TVariant lhs, const T& rhs) { + template ::value, int> = 0> + friend bool operator!=(TVariant lhs, const T& rhs) { return compare(lhs, rhs) != COMPARE_RESULT_EQUAL; } template - friend bool operator<(T* lhs, TVariant rhs) { + friend bool operator<(T* lhs, const TVariant& rhs) { return compare(rhs, lhs) == COMPARE_RESULT_GREATER; } template - friend bool operator<(const T& lhs, TVariant rhs) { + friend bool operator<(const T& lhs, const TVariant& rhs) { return compare(rhs, lhs) == COMPARE_RESULT_GREATER; } template - friend bool operator<(TVariant lhs, T* rhs) { + friend bool operator<(const TVariant& lhs, T* rhs) { return compare(lhs, rhs) == COMPARE_RESULT_LESS; } - template - friend enable_if_t::value, bool> operator<( - TVariant lhs, const T& rhs) { + template ::value, int> = 0> + friend bool operator<(TVariant lhs, const T& rhs) { return compare(lhs, rhs) == COMPARE_RESULT_LESS; } template - friend bool operator<=(T* lhs, TVariant rhs) { + friend bool operator<=(T* lhs, const TVariant& rhs) { return (compare(rhs, lhs) & COMPARE_RESULT_GREATER_OR_EQUAL) != 0; } template - friend bool operator<=(const T& lhs, TVariant rhs) { + friend bool operator<=(const T& lhs, const TVariant& rhs) { return (compare(rhs, lhs) & COMPARE_RESULT_GREATER_OR_EQUAL) != 0; } template - friend bool operator<=(TVariant lhs, T* rhs) { + friend bool operator<=(const TVariant& lhs, T* rhs) { return (compare(lhs, rhs) & COMPARE_RESULT_LESS_OR_EQUAL) != 0; } - template - friend enable_if_t::value, bool> - operator<=(TVariant lhs, const T& rhs) { + template ::value, int> = 0> + friend bool operator<=(TVariant lhs, const T& rhs) { return (compare(lhs, rhs) & COMPARE_RESULT_LESS_OR_EQUAL) != 0; } template - friend bool operator>(T* lhs, TVariant rhs) { + friend bool operator>(T* lhs, const TVariant& rhs) { return compare(rhs, lhs) == COMPARE_RESULT_LESS; } template - friend bool operator>(const T& lhs, TVariant rhs) { + friend bool operator>(const T& lhs, const TVariant& rhs) { return compare(rhs, lhs) == COMPARE_RESULT_LESS; } template - friend bool operator>(TVariant lhs, T* rhs) { + friend bool operator>(const TVariant& lhs, T* rhs) { return compare(lhs, rhs) == COMPARE_RESULT_GREATER; } - template - friend enable_if_t::value, bool> operator>( - TVariant lhs, const T& rhs) { + template ::value, int> = 0> + friend bool operator>(TVariant lhs, const T& rhs) { return compare(lhs, rhs) == COMPARE_RESULT_GREATER; } template - friend bool operator>=(T* lhs, TVariant rhs) { + friend bool operator>=(T* lhs, const TVariant& rhs) { return (compare(rhs, lhs) & COMPARE_RESULT_LESS_OR_EQUAL) != 0; } template - friend bool operator>=(const T& lhs, TVariant rhs) { + friend bool operator>=(const T& lhs, const TVariant& rhs) { return (compare(rhs, lhs) & COMPARE_RESULT_LESS_OR_EQUAL) != 0; } template - friend bool operator>=(TVariant lhs, T* rhs) { + friend bool operator>=(const TVariant& lhs, T* rhs) { return (compare(lhs, rhs) & COMPARE_RESULT_GREATER_OR_EQUAL) != 0; } - template - friend enable_if_t::value, bool> - operator>=(TVariant lhs, const T& rhs) { + template ::value, int> = 0> + friend bool operator>=(const TVariant& lhs, const T& rhs) { return (compare(lhs, rhs) & COMPARE_RESULT_GREATER_OR_EQUAL) != 0; } }; @@ -2954,72 +2952,78 @@ class JsonVariantConst : public detail::VariantTag, return detail::VariantData::size(data_, resources_); } template ::value, bool> = true> + detail::enable_if_t::value, int> = 0> T as() const { return Converter::fromJson(*this); } template ::value, bool> = true> + detail::enable_if_t::value, int> = 0> detail::InvalidConversion as() const; - template - detail::enable_if_t::value, bool> is() const { + template ::value, int> = 0> + bool is() const { return Converter::checkJson(*this); } - template - detail::enable_if_t::value, bool> is() const { + template ::value, int> = 0> + bool is() const { return false; } template operator T() const { return as(); } - template - detail::enable_if_t::value, JsonVariantConst> - operator[](T index) const { + template ::value, int> = 0> + JsonVariantConst operator[](T index) const { return JsonVariantConst( detail::VariantData::getElement(data_, size_t(index), resources_), resources_); } - template - detail::enable_if_t::value, JsonVariantConst> - operator[](const TString& key) const { + template ::value, int> = 0> + JsonVariantConst operator[](const TString& key) const { return JsonVariantConst(detail::VariantData::getMember( data_, detail::adaptString(key), resources_), resources_); } - template - detail::enable_if_t::value, JsonVariantConst> - operator[](TChar* key) const { + template ::value && + !detail::is_const::value, + int> = 0> + JsonVariantConst operator[](TChar* key) const { return JsonVariantConst(detail::VariantData::getMember( data_, detail::adaptString(key), resources_), resources_); } - template - detail::enable_if_t::value, JsonVariantConst> - operator[](const TVariant& key) const { + template ::value, int> = 0> + JsonVariantConst operator[](const TVariant& key) const { if (key.template is()) return operator[](key.template as()); else return operator[](key.template as()); } - template + template ::value, int> = 0> ARDUINOJSON_DEPRECATED("use var[key].is() instead") - detail::enable_if_t::value, bool> containsKey( - const TString& key) const { + bool containsKey(const TString& key) const { return detail::VariantData::getMember(getData(), detail::adaptString(key), resources_) != 0; } - template + template ::value && + !detail::is_const::value, + int> = 0> ARDUINOJSON_DEPRECATED("use obj[\"key\"].is() instead") - detail::enable_if_t::value, bool> containsKey( - TChar* key) const { + bool containsKey(TChar* key) const { return detail::VariantData::getMember(getData(), detail::adaptString(key), resources_) != 0; } - template + template ::value, int> = 0> ARDUINOJSON_DEPRECATED("use var[key].is() instead") - detail::enable_if_t::value, bool> containsKey( - const TVariant& key) const { + bool containsKey(const TVariant& key) const { return containsKey(key.template as()); } ARDUINOJSON_DEPRECATED("always returns zero") @@ -3059,23 +3063,26 @@ class VariantRefBase : public VariantTag { } template T as() const; - template ::value>> + template ::value, int> = 0> operator T() const { return as(); } - template - enable_if_t::value, JsonArray> to() const; - template - enable_if_t::value, JsonObject> to() const; - template - enable_if_t::value, JsonVariant> to() const; + template ::value, int> = 0> + JsonArray to() const; + template ::value, int> = 0> + JsonObject to() const; + template ::value, int> = 0> + JsonVariant to() const; template FORCE_INLINE bool is() const; template bool set(const T& value) const { - return doSet>>(value); + using TypeForConverter = conditional_t::value, T, + remove_cv_t>>; + return doSet>(value); } - template + template ::value, int> = 0> bool set(T* value) const { return doSet>(value); } @@ -3085,18 +3092,18 @@ class VariantRefBase : public VariantTag { size_t nesting() const { return VariantData::nesting(getData(), getResourceManager()); } - template - enable_if_t::value, T> add() const { + template ::value, int> = 0> + T add() const { return add().template to(); } - template - enable_if_t::value, T> add() const; + template ::value, int> = 0> + T add() const; template bool add(const T& value) const { return detail::VariantData::addValue(getOrCreateData(), value, getResourceManager()); } - template + template ::value, int> = 0> bool add(T* value) const { return detail::VariantData::addValue(getOrCreateData(), value, getResourceManager()); @@ -3104,46 +3111,43 @@ class VariantRefBase : public VariantTag { void remove(size_t index) const { VariantData::removeElement(getData(), index, getResourceManager()); } - template - enable_if_t::value> remove(TChar* key) const { + template ::value, int> = 0> + void remove(TChar* key) const { VariantData::removeMember(getData(), adaptString(key), getResourceManager()); } - template - enable_if_t::value> remove(const TString& key) const { + template ::value, int> = 0> + void remove(const TString& key) const { VariantData::removeMember(getData(), adaptString(key), getResourceManager()); } - template - enable_if_t::value> remove(const TVariant& key) const { + template ::value, int> = 0> + void remove(const TVariant& key) const { if (key.template is()) remove(key.template as()); else remove(key.template as()); } ElementProxy operator[](size_t index) const; - template + template ::value, int> = 0> ARDUINOJSON_DEPRECATED("use obj[key].is() instead") - enable_if_t::value, bool> containsKey( - const TString& key) const; - template + bool containsKey(const TString& key) const; + template ::value, int> = 0> ARDUINOJSON_DEPRECATED("use obj[\"key\"].is() instead") - enable_if_t::value, bool> containsKey(TChar* key) const; - template + bool containsKey(TChar* key) const; + template ::value, int> = 0> ARDUINOJSON_DEPRECATED("use obj[key].is() instead") - enable_if_t::value, bool> containsKey( - const TVariant& key) const; - template - FORCE_INLINE - enable_if_t::value, MemberProxy> - operator[](const TString& key) const; - template - FORCE_INLINE - enable_if_t::value, MemberProxy> - operator[](TChar* key) const; - template - enable_if_t::value, JsonVariantConst> operator[]( - const TVariant& key) const { + bool containsKey(const TVariant& key) const; + template ::value, int> = 0> + FORCE_INLINE MemberProxy> operator[]( + const TString& key) const; + template < + typename TChar, + enable_if_t::value && !is_const::value, int> = 0> + FORCE_INLINE MemberProxy> operator[]( + TChar* key) const; + template ::value, int> = 0> + JsonVariantConst operator[](const TVariant& key) const { if (key.template is()) return operator[](key.template as()); else @@ -3206,28 +3210,30 @@ class VariantRefBase : public VariantTag { return getVariant(); } template - bool doSet(T&& value) const { + bool doSet(const T& value) const { return doSet( - detail::forward(value), - is_same::return_type, - bool>{}); + value, is_same::return_type, + bool>{}); } template - bool doSet(T&& value, false_type) const; + bool doSet(const T& value, false_type) const; template - bool doSet(T&& value, true_type) const; + bool doSet(const T& value, true_type) const; ArduinoJson::JsonVariant getOrCreateVariant() const; }; template class ElementProxy : public VariantRefBase>, public VariantOperators> { friend class VariantAttorney; + friend class VariantRefBase>; + template + friend class MemberProxy; + template + friend class ElementProxy; public: ElementProxy(TUpstream upstream, size_t index) : upstream_(upstream), index_(index) {} - ElementProxy(const ElementProxy& src) - : upstream_(src.upstream_), index_(src.index_) {} ElementProxy& operator=(const ElementProxy& src) { this->set(src); return *this; @@ -3243,6 +3249,8 @@ class ElementProxy : public VariantRefBase>, return *this; } private: + ElementProxy(const ElementProxy& src) // Error here? See https://arduinojson.org/v7/proxy-non-copyable/ + : upstream_(src.upstream_), index_(src.index_) {} ResourceManager* getResourceManager() const { return VariantAttorney::getResourceManager(upstream_); } @@ -3397,16 +3405,16 @@ class JsonArrayConst : public detail::VariantOperators { JsonArrayConst(const detail::ArrayData* data, const detail::ResourceManager* resources) : data_(data), resources_(resources) {} - template - detail::enable_if_t::value, JsonVariantConst> - operator[](T index) const { + template ::value, int> = 0> + JsonVariantConst operator[](T index) const { return JsonVariantConst( detail::ArrayData::getElement(data_, size_t(index), resources_), resources_); } - template - detail::enable_if_t::value, JsonVariantConst> - operator[](const TVariant& variant) const { + template ::value, int> = 0> + JsonVariantConst operator[](const TVariant& variant) const { if (variant.template is()) return operator[](variant.template as()); else @@ -3472,12 +3480,14 @@ class JsonArray : public detail::VariantOperators { operator JsonArrayConst() const { return JsonArrayConst(data_, resources_); } - template - detail::enable_if_t::value, T> add() const { + template ::value, int> = 0> + T add() const { return add().to(); } - template - detail::enable_if_t::value, T> add() const { + template ::value, int> = 0> + JsonVariant add() const { return JsonVariant(detail::ArrayData::addElement(data_, resources_), resources_); } @@ -3485,7 +3495,8 @@ class JsonArray : public detail::VariantOperators { bool add(const T& value) const { return detail::ArrayData::addValue(data_, value, resources_); } - template + template ::value, int> = 0> bool add(T* value) const { return detail::ArrayData::addValue(data_, value, resources_); } @@ -3513,27 +3524,25 @@ class JsonArray : public detail::VariantOperators { void remove(size_t index) const { detail::ArrayData::removeElement(data_, index, resources_); } - template - detail::enable_if_t::value> remove( - TVariant variant) const { + template ::value, int> = 0> + void remove(const TVariant& variant) const { if (variant.template is()) remove(variant.template as()); } void clear() const { detail::ArrayData::clear(data_, resources_); } - template - detail::enable_if_t::value, - detail::ElementProxy> - operator[](T index) const { + template ::value, int> = 0> + detail::ElementProxy operator[](T index) const { return {*this, size_t(index)}; } - template - detail::enable_if_t::value, - detail::ElementProxy> - operator[](const TVariant& variant) const { + template ::value, int> = 0> + detail::ElementProxy operator[](const TVariant& variant) const { if (variant.template is()) - return operator[](variant.template as()); + return {*this, variant.template as()}; else return {*this, size_t(-1)}; } @@ -3707,10 +3716,10 @@ class JsonObjectConst : public detail::VariantOperators { iterator end() const { return iterator(); } - template + template ::value, int> = 0> ARDUINOJSON_DEPRECATED("use obj[key].is() instead") - detail::enable_if_t::value, bool> containsKey( - const TString& key) const { + bool containsKey(const TString& key) const { return detail::ObjectData::getMember(data_, detail::adaptString(key), resources_) != 0; } @@ -3720,29 +3729,31 @@ class JsonObjectConst : public detail::VariantOperators { return detail::ObjectData::getMember(data_, detail::adaptString(key), resources_) != 0; } - template + template ::value, int> = 0> ARDUINOJSON_DEPRECATED("use obj[key].is() instead") - detail::enable_if_t::value, bool> containsKey( - const TVariant& key) const { + bool containsKey(const TVariant& key) const { return containsKey(key.template as()); } - template - detail::enable_if_t::value, JsonVariantConst> - operator[](const TString& key) const { + template ::value, int> = 0> + JsonVariantConst operator[](const TString& key) const { return JsonVariantConst(detail::ObjectData::getMember( data_, detail::adaptString(key), resources_), resources_); } - template - detail::enable_if_t::value, JsonVariantConst> - operator[](TChar* key) const { + template ::value && + !detail::is_const::value, + int> = 0> + JsonVariantConst operator[](TChar* key) const { return JsonVariantConst(detail::ObjectData::getMember( data_, detail::adaptString(key), resources_), resources_); } - template - detail::enable_if_t::value, JsonVariantConst> - operator[](const TVariant& key) const { + template ::value, int> = 0> + JsonVariantConst operator[](const TVariant& key) const { if (key.template is()) return operator[](key.template as()); else @@ -3777,16 +3788,19 @@ inline bool operator==(JsonObjectConst lhs, JsonObjectConst rhs) { } ARDUINOJSON_END_PUBLIC_NAMESPACE ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE -template +template class MemberProxy - : public VariantRefBase>, - public VariantOperators> { + : public VariantRefBase>, + public VariantOperators> { friend class VariantAttorney; + friend class VariantRefBase>; + template + friend class MemberProxy; + template + friend class ElementProxy; public: - MemberProxy(TUpstream upstream, TStringRef key) + MemberProxy(TUpstream upstream, AdaptedString key) : upstream_(upstream), key_(key) {} - MemberProxy(const MemberProxy& src) - : upstream_(src.upstream_), key_(src.key_) {} MemberProxy& operator=(const MemberProxy& src) { this->set(src); return *this; @@ -3796,30 +3810,32 @@ class MemberProxy this->set(src); return *this; } - template + template ::value, int> = 0> MemberProxy& operator=(T* src) { this->set(src); return *this; } private: + MemberProxy(const MemberProxy& src) // Error here? See https://arduinojson.org/v7/proxy-non-copyable/ + : upstream_(src.upstream_), key_(src.key_) {} ResourceManager* getResourceManager() const { return VariantAttorney::getResourceManager(upstream_); } VariantData* getData() const { return VariantData::getMember( - VariantAttorney::getData(upstream_), adaptString(key_), + VariantAttorney::getData(upstream_), key_, VariantAttorney::getResourceManager(upstream_)); } VariantData* getOrCreateData() const { auto data = VariantAttorney::getOrCreateData(upstream_); if (!data) return nullptr; - return data->getOrAddMember(adaptString(key_), + return data->getOrAddMember(key_, VariantAttorney::getResourceManager(upstream_)); } private: TUpstream upstream_; - TStringRef key_; + AdaptedString key_; }; ARDUINOJSON_END_PRIVATE_NAMESPACE ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE @@ -3875,39 +3891,38 @@ class JsonObject : public detail::VariantOperators { } return true; } - template - detail::enable_if_t::value, - detail::MemberProxy> - operator[](const TString& key) const { - return {*this, key}; + template ::value, int> = 0> + detail::MemberProxy> operator[]( + const TString& key) const { + return {*this, detail::adaptString(key)}; } - template - detail::enable_if_t::value, - detail::MemberProxy> - operator[](TChar* key) const { - return {*this, key}; - } - template - detail::enable_if_t::value, - detail::MemberProxy> - operator[](const TVariant& key) const { - if (key.template is()) - return {*this, key.template as()}; - else - return {*this, nullptr}; + template ::value && + !detail::is_const::value, + int> = 0> + detail::MemberProxy> operator[]( + TChar* key) const { + return {*this, detail::adaptString(key)}; + } + template ::value, int> = 0> + detail::MemberProxy> operator[]( + const TVariant& key) const { + return {*this, detail::adaptString(key.template as())}; } FORCE_INLINE void remove(iterator it) const { detail::ObjectData::remove(data_, it.iterator_, resources_); } - template - detail::enable_if_t::value> remove( - const TString& key) const { + template ::value, int> = 0> + void remove(const TString& key) const { detail::ObjectData::removeMember(data_, detail::adaptString(key), resources_); } - template - detail::enable_if_t::value> remove( - const TVariant& key) const { + template ::value, int> = 0> + void remove(const TVariant& key) const { if (key.template is()) remove(key.template as()); } @@ -3916,24 +3931,26 @@ class JsonObject : public detail::VariantOperators { detail::ObjectData::removeMember(data_, detail::adaptString(key), resources_); } - template + template ::value, int> = 0> ARDUINOJSON_DEPRECATED("use obj[key].is() instead") - detail::enable_if_t::value, bool> containsKey( - const TString& key) const { + bool containsKey(const TString& key) const { return detail::ObjectData::getMember(data_, detail::adaptString(key), resources_) != 0; } - template + template ::value && + !detail::is_const::value, + int> = 0> ARDUINOJSON_DEPRECATED("use obj[\"key\"].is() instead") - detail::enable_if_t::value, bool> containsKey( - TChar* key) const { + bool containsKey(TChar* key) const { return detail::ObjectData::getMember(data_, detail::adaptString(key), resources_) != 0; } - template + template ::value, int> = 0> ARDUINOJSON_DEPRECATED("use obj[key].is() instead") - detail::enable_if_t::value, bool> containsKey( - const TVariant& key) const { + bool containsKey(const TVariant& key) const { return containsKey(key.template as()); } template @@ -3985,14 +4002,15 @@ class JsonDocument : public detail::VariantOperators { : JsonDocument(detail::DefaultAllocator::instance()) { swap(*this, src); } - template - JsonDocument( - const T& src, Allocator* alloc = detail::DefaultAllocator::instance(), - detail::enable_if_t::value || - detail::is_same::value || - detail::is_same::value || - detail::is_same::value || - detail::is_same::value>* = 0) + template ::value || + detail::is_same::value || + detail::is_same::value || + detail::is_same::value || + detail::is_same::value, + int> = 0> + JsonDocument(const T& src, + Allocator* alloc = detail::DefaultAllocator::instance()) : JsonDocument(alloc) { set(src); } @@ -4046,12 +4064,14 @@ class JsonDocument : public detail::VariantOperators { bool set(const JsonDocument& src) { return to().set(src.as()); } - template - detail::enable_if_t::value, bool> set( - const T& src) { + template < + typename T, + detail::enable_if_t::value, int> = 0> + bool set(const T& src) { return to().set(src); } - template + template ::value, int> = 0> bool set(TChar* src) { return to().set(src); } @@ -4065,95 +4085,105 @@ class JsonDocument : public detail::VariantOperators { bool containsKey(TChar* key) const { return data_.getMember(detail::adaptString(key), &resources_) != 0; } - template + template ::value, int> = 0> ARDUINOJSON_DEPRECATED("use doc[key].is() instead") - detail::enable_if_t::value, bool> containsKey( - const TString& key) const { + bool containsKey(const TString& key) const { return data_.getMember(detail::adaptString(key), &resources_) != 0; } - template + template ::value, int> = 0> ARDUINOJSON_DEPRECATED("use doc[key].is() instead") - detail::enable_if_t::value, bool> containsKey( - const TVariant& key) const { + bool containsKey(const TVariant& key) const { return containsKey(key.template as()); } - template - detail::enable_if_t::value, - detail::MemberProxy> - operator[](const TString& key) { - return {*this, key}; - } - template - detail::enable_if_t::value, - detail::MemberProxy> - operator[](TChar* key) { - return {*this, key}; - } - template - detail::enable_if_t::value, JsonVariantConst> - operator[](const TString& key) const { + template ::value, int> = 0> + detail::MemberProxy> operator[]( + const TString& key) { + return {*this, detail::adaptString(key)}; + } + template ::value && + !detail::is_const::value, + int> = 0> + detail::MemberProxy> operator[]( + TChar* key) { + return {*this, detail::adaptString(key)}; + } + template ::value, int> = 0> + JsonVariantConst operator[](const TString& key) const { return JsonVariantConst( data_.getMember(detail::adaptString(key), &resources_), &resources_); } - template - detail::enable_if_t::value, JsonVariantConst> - operator[](TChar* key) const { + template ::value && + !detail::is_const::value, + int> = 0> + JsonVariantConst operator[](TChar* key) const { return JsonVariantConst( data_.getMember(detail::adaptString(key), &resources_), &resources_); } - template - detail::enable_if_t::value, - detail::ElementProxy> - operator[](T index) { + template ::value, int> = 0> + detail::ElementProxy operator[](T index) { return {*this, size_t(index)}; } JsonVariantConst operator[](size_t index) const { return JsonVariantConst(data_.getElement(index, &resources_), &resources_); } - template - detail::enable_if_t::value, JsonVariantConst> - operator[](const TVariant& key) const { + template ::value, int> = 0> + JsonVariantConst operator[](const TVariant& key) const { if (key.template is()) return operator[](key.template as()); if (key.template is()) return operator[](key.template as()); return {}; } - template - detail::enable_if_t::value, T> add() { + template ::value, int> = 0> + T add() { return add().to(); } - template - detail::enable_if_t::value, T> add() { + template ::value, int> = 0> + JsonVariant add() { return JsonVariant(data_.addElement(&resources_), &resources_); } template bool add(const TValue& value) { return data_.addValue(value, &resources_); } - template + template ::value, int> = 0> bool add(TChar* value) { return data_.addValue(value, &resources_); } - template - detail::enable_if_t::value> remove(T index) { + template ::value, int> = 0> + void remove(T index) { detail::VariantData::removeElement(getData(), size_t(index), getResourceManager()); } - template - detail::enable_if_t::value> remove(TChar* key) { + template ::value && + !detail::is_const::value, + int> = 0> + void remove(TChar* key) { detail::VariantData::removeMember(getData(), detail::adaptString(key), getResourceManager()); } - template - detail::enable_if_t::value> remove( - const TString& key) { + template ::value, int> = 0> + void remove(const TString& key) { detail::VariantData::removeMember(getData(), detail::adaptString(key), getResourceManager()); } - template - detail::enable_if_t::value> remove( - const TVariant& key) { + template ::value, int> = 0> + void remove(const TVariant& key) { if (key.template is()) remove(key.template as()); if (key.template is()) @@ -4475,13 +4505,13 @@ inline void ArrayData::removeElement(size_t index, ResourceManager* resources) { remove(at(index, resources), resources); } template -inline bool ArrayData::addValue(T&& value, ResourceManager* resources) { +inline bool ArrayData::addValue(const T& value, ResourceManager* resources) { ARDUINOJSON_ASSERT(resources != nullptr); auto slot = resources->allocVariant(); if (!slot) return false; JsonVariant variant(slot.ptr(), resources); - if (!variant.set(detail::forward(value))) { + if (!variant.set(value)) { resources->freeVariant(slot); return false; } @@ -4493,21 +4523,20 @@ constexpr size_t sizeofArray(size_t n) { } ARDUINOJSON_END_PRIVATE_NAMESPACE ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE -template -inline detail::enable_if_t::value, bool> copyArray( - const T& src, JsonVariant dst) { +template ::value, int> = 0> +inline bool copyArray(const T& src, JsonVariant dst) { return dst.set(src); } -template -inline detail::enable_if_t< - !detail::is_base_of::value, bool> -copyArray(T (&src)[N], const TDestination& dst) { +template ::value, int> = 0> +inline bool copyArray(T (&src)[N], const TDestination& dst) { return copyArray(src, N, dst); } -template -inline detail::enable_if_t< - !detail::is_base_of::value, bool> -copyArray(const T* src, size_t len, const TDestination& dst) { +template ::value, int> = 0> +inline bool copyArray(const T* src, size_t len, const TDestination& dst) { bool ok = true; for (size_t i = 0; i < len; i++) { ok &= copyArray(src[i], dst.template add()); @@ -4526,9 +4555,8 @@ template inline bool copyArray(const T* src, size_t len, JsonDocument& dst) { return copyArray(src, len, dst.to()); } -template -inline detail::enable_if_t::value, size_t> copyArray( - JsonVariantConst src, T& dst) { +template ::value, int> = 0> +inline size_t copyArray(JsonVariantConst src, T& dst) { dst = src.as(); return 1; } @@ -4554,11 +4582,12 @@ inline size_t copyArray(JsonVariantConst src, char (&dst)[N]) { dst[len] = 0; return 1; } -template -inline detail::enable_if_t::value && - detail::is_base_of::value, - size_t> -copyArray(const TSource& src, T& dst) { +template < + typename TSource, typename T, + detail::enable_if_t::value && + detail::is_base_of::value, + int> = 0> +inline size_t copyArray(const TSource& src, T& dst) { return copyArray(src.template as(), dst); } ARDUINOJSON_END_PUBLIC_NAMESPACE @@ -5289,9 +5318,10 @@ class JsonSerializer : public VariantDataVisitor { }; ARDUINOJSON_END_PRIVATE_NAMESPACE ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE -template -detail::enable_if_t::value, size_t> -serializeJson(JsonVariantConst source, TDestination& destination) { +template < + typename TDestination, + detail::enable_if_t::value, int> = 0> +size_t serializeJson(JsonVariantConst source, TDestination& destination) { using namespace detail; return serialize(source, destination); } @@ -5305,10 +5335,10 @@ inline size_t measureJson(JsonVariantConst source) { return measure(source); } #if ARDUINOJSON_ENABLE_STD_STREAM -template -inline detail::enable_if_t::value, - std::ostream&> -operator<<(std::ostream& os, const T& source) { +template ::value, int> = 0> +inline std::ostream& operator<<(std::ostream& os, const T& source) { serializeJson(source, os); return os; } @@ -5365,7 +5395,7 @@ class StringBuilder { JsonString str() const { ARDUINOJSON_ASSERT(node_ != nullptr); node_->data[size_] = 0; - return JsonString(node_->data, size_, JsonString::Copied); + return JsonString(node_->data, size_); } private: ResourceManager* resources_; @@ -5388,7 +5418,7 @@ struct Converter { static void toJson(const T& src, JsonVariant dst) { convertToJson(src, dst); // Error here? See https://arduinojson.org/v7/unsupported-set/ } - static T fromJson(JsonVariantConst src) { + static detail::decay_t fromJson(JsonVariantConst src) { static_assert(!detail::is_same::value, "type 'char*' is not supported, use 'const char*' instead"); T result; // Error here? See https://arduinojson.org/v7/non-default-constructible/ @@ -5511,7 +5541,7 @@ struct Converter : private detail::VariantAttorney { } static JsonString fromJson(JsonVariantConst src) { auto data = getData(src); - return data ? data->asString() : 0; + return data ? data->asString() : JsonString(); } static bool checkJson(JsonVariantConst src) { auto data = getData(src); @@ -5728,7 +5758,7 @@ inline bool VariantData::setString(TAdaptedString value, ARDUINOJSON_ASSERT(type_ == VariantType::Null); // must call clear() first if (value.isNull()) return false; - if (value.isLinked()) { + if (value.isStatic()) { setLinkedString(value.data()); return true; } @@ -5873,31 +5903,27 @@ inline void convertToJson(const VariantRefBase& src, dst.set(src.template as()); } template -template -inline enable_if_t::value, T> -VariantRefBase::add() const { +template ::value, int>> +inline T VariantRefBase::add() const { return JsonVariant( detail::VariantData::addElement(getOrCreateData(), getResourceManager()), getResourceManager()); } template -template -inline enable_if_t::value, bool> -VariantRefBase::containsKey(const TString& key) const { +template ::value, int>> +inline bool VariantRefBase::containsKey(const TString& key) const { return VariantData::getMember(getData(), adaptString(key), getResourceManager()) != 0; } template -template -inline enable_if_t::value, bool> -VariantRefBase::containsKey(TChar* key) const { +template ::value, int>> +inline bool VariantRefBase::containsKey(TChar* key) const { return VariantData::getMember(getData(), adaptString(key), getResourceManager()) != 0; } template -template -inline enable_if_t::value, bool> -VariantRefBase::containsKey(const TVariant& key) const { +template ::value, int>> +inline bool VariantRefBase::containsKey(const TVariant& key) const { return containsKey(key.template as()); } template @@ -5918,52 +5944,50 @@ inline bool VariantRefBase::is() const { template inline ElementProxy VariantRefBase::operator[]( size_t index) const { - return ElementProxy(derived(), index); + return {derived(), index}; } template -template -inline enable_if_t::value, MemberProxy> -VariantRefBase::operator[](TString* key) const { - return MemberProxy(derived(), key); +template ::value && !is_const::value, int>> +inline MemberProxy> +VariantRefBase::operator[](TChar* key) const { + return {derived(), adaptString(key)}; } template -template -inline enable_if_t::value, MemberProxy> +template ::value, int>> +inline MemberProxy> VariantRefBase::operator[](const TString& key) const { - return MemberProxy(derived(), key); + return {derived(), adaptString(key)}; } template template -inline bool VariantRefBase::doSet(T&& value, false_type) const { +inline bool VariantRefBase::doSet(const T& value, false_type) const { TConverter::toJson(value, getOrCreateVariant()); auto resources = getResourceManager(); return resources && !resources->overflowed(); } template template -inline bool VariantRefBase::doSet(T&& value, true_type) const { +inline bool VariantRefBase::doSet(const T& value, true_type) const { return TConverter::toJson(value, getOrCreateVariant()); } template -template -inline enable_if_t::value, JsonArray> -VariantRefBase::to() const { +template ::value, int>> +inline JsonArray VariantRefBase::to() const { return JsonArray( VariantData::toArray(getOrCreateData(), getResourceManager()), getResourceManager()); } template -template -enable_if_t::value, JsonObject> -VariantRefBase::to() const { +template ::value, int>> +JsonObject VariantRefBase::to() const { return JsonObject( VariantData::toObject(getOrCreateData(), getResourceManager()), getResourceManager()); } template -template -enable_if_t::value, JsonVariant> -VariantRefBase::to() const { +template ::value, int>> +JsonVariant VariantRefBase::to() const { auto data = getOrCreateData(); auto resources = getResourceManager(); detail::VariantData::clear(data, resources); @@ -6347,10 +6371,11 @@ DeserializationError doDeserialize(TDestination&& dst, TReader reader, shrinkJsonDocument(dst); return err; } -template