diff --git a/interface/package.json b/interface/package.json index 6f5a18cc6..b3adefc58 100644 --- a/interface/package.json +++ b/interface/package.json @@ -30,7 +30,7 @@ "@mui/material": "^5.15.19", "@table-library/react-table-library": "4.1.7", "@types/lodash-es": "^4.17.12", - "@types/node": "^20.14.1", + "@types/node": "^20.14.2", "@types/react": "^18.3.3", "@types/react-dom": "^18.3.0", "@types/react-router-dom": "^5.3.3", @@ -50,7 +50,7 @@ "typescript": "^5.4.5" }, "devDependencies": { - "@babel/core": "^7.24.6", + "@babel/core": "^7.24.7", "@eslint/js": "^9.4.0", "@preact/compat": "^17.1.2", "@preact/preset-vite": "^2.8.2", @@ -60,11 +60,11 @@ "eslint": "^9.4.0", "eslint-config-prettier": "^9.1.0", "preact": "^10.22.0", - "prettier": "^3.3.0", + "prettier": "^3.3.1", "rollup-plugin-visualizer": "^5.12.0", - "terser": "^5.31.0", + "terser": "^5.31.1", "typescript-eslint": "^7.12.0", - "vite": "^5.2.12", + "vite": "^5.2.13", "vite-plugin-imagemin": "^0.6.1", "vite-tsconfig-paths": "^4.3.2" }, diff --git a/interface/src/framework/system/SystemStatus.tsx b/interface/src/framework/system/SystemStatus.tsx index 4b616fac4..73f5db67d 100644 --- a/interface/src/framework/system/SystemStatus.tsx +++ b/interface/src/framework/system/SystemStatus.tsx @@ -10,8 +10,10 @@ import MemoryIcon from '@mui/icons-material/Memory'; import PermScanWifiIcon from '@mui/icons-material/PermScanWifi'; import PowerSettingsNewIcon from '@mui/icons-material/PowerSettingsNew'; import RefreshIcon from '@mui/icons-material/Refresh'; +import RouterIcon from '@mui/icons-material/Router'; import SettingsInputAntennaIcon from '@mui/icons-material/SettingsInputAntenna'; import TimerIcon from '@mui/icons-material/Timer'; +import WifiIcon from '@mui/icons-material/Wifi'; import { Avatar, Box, @@ -38,7 +40,7 @@ import ListMenuItem from 'components/layout/ListMenuItem'; import { AuthenticatedContext } from 'contexts/authentication'; import { useI18nContext } from 'i18n/i18n-react'; import { busConnectionStatus } from 'project/types'; -import { NTPSyncStatus } from 'types'; +import { NTPSyncStatus, NetworkConnectionStatus } from 'types'; import RestartMonitor from './RestartMonitor'; @@ -151,6 +153,46 @@ const SystemStatus: FC = () => { } }; + const networkStatusHighlight = () => { + switch (data.network_status) { + case NetworkConnectionStatus.WIFI_STATUS_IDLE: + case NetworkConnectionStatus.WIFI_STATUS_DISCONNECTED: + case NetworkConnectionStatus.WIFI_STATUS_NO_SHIELD: + return theme.palette.info.main; + case NetworkConnectionStatus.WIFI_STATUS_CONNECTED: + case NetworkConnectionStatus.ETHERNET_STATUS_CONNECTED: + return theme.palette.success.main; + case NetworkConnectionStatus.WIFI_STATUS_CONNECT_FAILED: + case NetworkConnectionStatus.WIFI_STATUS_CONNECTION_LOST: + return theme.palette.error.main; + default: + return theme.palette.warning.main; + } + }; + + const networkStatus = () => { + switch (data.network_status) { + case NetworkConnectionStatus.WIFI_STATUS_NO_SHIELD: + return LL.INACTIVE(1); + case NetworkConnectionStatus.WIFI_STATUS_IDLE: + return LL.IDLE(); + case NetworkConnectionStatus.WIFI_STATUS_NO_SSID_AVAIL: + return 'No SSID Available'; + case NetworkConnectionStatus.WIFI_STATUS_CONNECTED: + return LL.CONNECTED(0) + ' (WiFi, ' + data.wifi_rssi + ' dBm)'; + case NetworkConnectionStatus.ETHERNET_STATUS_CONNECTED: + return LL.CONNECTED(0) + ' (Ethernet)'; + case NetworkConnectionStatus.WIFI_STATUS_CONNECT_FAILED: + return LL.CONNECTED(1) + ' ' + LL.FAILED(0); + case NetworkConnectionStatus.WIFI_STATUS_CONNECTION_LOST: + return LL.CONNECTED(1) + ' ' + LL.LOST(); + case NetworkConnectionStatus.WIFI_STATUS_DISCONNECTED: + return LL.DISCONNECTED(); + default: + return LL.UNKNOWN(); + } + }; + const activeHighlight = (value: boolean) => value ? theme.palette.success.main : theme.palette.info.main; @@ -296,7 +338,7 @@ const SystemStatus: FC = () => { + + ) : ( + + + {numChanges !== 0 && ( + + + + + )} + + )} - + ); }; return ( {blocker ? : null} - - - Activate or de-activate EMS-ESP library modules by selecting (** - experimental **) - - - {renderModules()} - - - - {numChanges !== 0 && ( - - - - - )} - - + {restarting ? : renderContent()} + {selectedModuleItem && ( + + )} ); }; diff --git a/interface/src/project/ModulesDialog.tsx b/interface/src/project/ModulesDialog.tsx new file mode 100644 index 000000000..f3293ab7a --- /dev/null +++ b/interface/src/project/ModulesDialog.tsx @@ -0,0 +1,108 @@ +import { useEffect, useState } from 'react'; + +import CancelIcon from '@mui/icons-material/Cancel'; +import DoneIcon from '@mui/icons-material/Done'; +import { + Box, + Button, + Checkbox, + Dialog, + DialogActions, + DialogContent, + DialogTitle, + Grid, + TextField +} from '@mui/material'; + +import { dialogStyle } from 'CustomTheme'; +import { BlockFormControlLabel } from 'components'; +import { useI18nContext } from 'i18n/i18n-react'; +import { updateValue } from 'utils'; + +import type { ModuleItem } from './types'; + +interface ModulesDialogProps { + open: boolean; + onClose: () => void; + onSave: (mi: ModuleItem) => void; + selectedItem: ModuleItem; +} + +const ModulesDialog = ({ + open, + onClose, + onSave, + selectedItem +}: ModulesDialogProps) => { + const { LL } = useI18nContext(); + const [editItem, setEditItem] = useState(selectedItem); + + const updateFormValue = updateValue(setEditItem); + + useEffect(() => { + if (open) { + setEditItem(selectedItem); + } + }, [open, selectedItem]); + + const close = () => { + onClose(); + }; + + const save = () => { + onSave(editItem); + }; + + return ( + + + {LL.EDIT() + ' ' + LL.MODULES() + ' : ' + editItem.key} + + + + + } + label={LL.ACTIVE()} + /> + + + + + + + + + + + ); +}; + +export default ModulesDialog; diff --git a/interface/src/project/api.ts b/interface/src/project/api.ts index 9a778c839..ae3336361 100644 --- a/interface/src/project/api.ts +++ b/interface/src/project/api.ts @@ -113,7 +113,8 @@ export const readModules = () => transformData(data) { return (data as Modules).modules.map((mi: ModuleItem) => ({ ...mi, - o_enabled: mi.enabled + o_enabled: mi.enabled, + o_license: mi.license })); } }); diff --git a/interface/src/project/types.ts b/interface/src/project/types.ts index e864e5ba8..c2a28423b 100644 --- a/interface/src/project/types.ts +++ b/interface/src/project/types.ts @@ -310,12 +310,16 @@ export interface Schedule { export interface ModuleItem { id: number; // unique index + key: string; name: string; author: string; version: string; - status: string; + status: number; + message: string; enabled: boolean; + license: string; o_enabled?: boolean; + o_license?: string; } export interface Modules { diff --git a/interface/src/types/system.ts b/interface/src/types/system.ts index de1616e0b..beb30e6a9 100644 --- a/interface/src/types/system.ts +++ b/interface/src/types/system.ts @@ -1,5 +1,7 @@ import type { busConnectionStatus } from 'project/types'; +import type { NetworkConnectionStatus } from './network'; + export interface ESPSystemStatus { emsesp_version: string; esp_platform: string; @@ -37,6 +39,8 @@ export interface SystemStatus { ntp_status: number; mqtt_status: boolean; ap_status: boolean; + network_status: NetworkConnectionStatus; + wifi_rssi: number; } export enum LogLevel { diff --git a/interface/yarn.lock b/interface/yarn.lock index efbd3cad2..829ef28bd 100644 --- a/interface/yarn.lock +++ b/interface/yarn.lock @@ -39,13 +39,13 @@ __metadata: languageName: node linkType: hard -"@babel/code-frame@npm:^7.24.6": - version: 7.24.6 - resolution: "@babel/code-frame@npm:7.24.6" +"@babel/code-frame@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/code-frame@npm:7.24.7" dependencies: - "@babel/highlight": "npm:^7.24.6" + "@babel/highlight": "npm:^7.24.7" picocolors: "npm:^1.0.0" - checksum: 10c0/c93c6d1763530f415218c31d07359364397f19b70026abdff766164c21ed352a931cf07f3102c5fb9e04792de319e332d68bcb1f7debef601a02197f90f9ba24 + checksum: 10c0/ab0af539473a9f5aeaac7047e377cb4f4edd255a81d84a76058595f8540784cc3fbe8acf73f1e073981104562490aabfb23008cd66dc677a456a4ed5390fdde6 languageName: node linkType: hard @@ -56,10 +56,10 @@ __metadata: languageName: node linkType: hard -"@babel/compat-data@npm:^7.24.6": - version: 7.24.6 - resolution: "@babel/compat-data@npm:7.24.6" - checksum: 10c0/f50abbd4008eb2a5d12139c578809cebbeaeb8e660fb12d546eb2e7c2108ae1836ab8339184a5f5ce0e95bf81bb91e18edce86b387c59db937b01693ec0bc774 +"@babel/compat-data@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/compat-data@npm:7.24.7" + checksum: 10c0/dcd93a5632b04536498fbe2be5af1057f635fd7f7090483d8e797878559037e5130b26862ceb359acbae93ed27e076d395ddb4663db6b28a665756ffd02d324f languageName: node linkType: hard @@ -86,26 +86,26 @@ __metadata: languageName: node linkType: hard -"@babel/core@npm:^7.24.6": - version: 7.24.6 - resolution: "@babel/core@npm:7.24.6" +"@babel/core@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/core@npm:7.24.7" dependencies: "@ampproject/remapping": "npm:^2.2.0" - "@babel/code-frame": "npm:^7.24.6" - "@babel/generator": "npm:^7.24.6" - "@babel/helper-compilation-targets": "npm:^7.24.6" - "@babel/helper-module-transforms": "npm:^7.24.6" - "@babel/helpers": "npm:^7.24.6" - "@babel/parser": "npm:^7.24.6" - "@babel/template": "npm:^7.24.6" - "@babel/traverse": "npm:^7.24.6" - "@babel/types": "npm:^7.24.6" + "@babel/code-frame": "npm:^7.24.7" + "@babel/generator": "npm:^7.24.7" + "@babel/helper-compilation-targets": "npm:^7.24.7" + "@babel/helper-module-transforms": "npm:^7.24.7" + "@babel/helpers": "npm:^7.24.7" + "@babel/parser": "npm:^7.24.7" + "@babel/template": "npm:^7.24.7" + "@babel/traverse": "npm:^7.24.7" + "@babel/types": "npm:^7.24.7" convert-source-map: "npm:^2.0.0" debug: "npm:^4.1.0" gensync: "npm:^1.0.0-beta.2" json5: "npm:^2.2.3" semver: "npm:^6.3.1" - checksum: 10c0/e0762a8daef7f417494d555929418cfacd6848c7fc3310ec00e6dd8cecac20b7f590e760bfc9365d2af07874a3f5599832f9c9ff7f1a9d126a168f77ba67945a + checksum: 10c0/4004ba454d3c20a46ea66264e06c15b82e9f6bdc35f88819907d24620da70dbf896abac1cb4cc4b6bb8642969e45f4d808497c9054a1388a386cf8c12e9b9e0d languageName: node linkType: hard @@ -132,15 +132,15 @@ __metadata: languageName: node linkType: hard -"@babel/generator@npm:^7.24.6": - version: 7.24.6 - resolution: "@babel/generator@npm:7.24.6" +"@babel/generator@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/generator@npm:7.24.7" dependencies: - "@babel/types": "npm:^7.24.6" + "@babel/types": "npm:^7.24.7" "@jridgewell/gen-mapping": "npm:^0.3.5" "@jridgewell/trace-mapping": "npm:^0.3.25" jsesc: "npm:^2.5.1" - checksum: 10c0/8d71a17b386536582354afba53cc784396458a88cc9f05f0c6de0ec99475f6f539943b3566b2e733820c4928236952473831765e483c25d68cc007a6e604d782 + checksum: 10c0/06b1f3350baf527a3309e50ffd7065f7aee04dd06e1e7db794ddfde7fe9d81f28df64edd587173f8f9295496a7ddb74b9a185d4bf4de7bb619e6d4ec45c8fd35 languageName: node linkType: hard @@ -166,16 +166,16 @@ __metadata: languageName: node linkType: hard -"@babel/helper-compilation-targets@npm:^7.24.6": - version: 7.24.6 - resolution: "@babel/helper-compilation-targets@npm:7.24.6" +"@babel/helper-compilation-targets@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/helper-compilation-targets@npm:7.24.7" dependencies: - "@babel/compat-data": "npm:^7.24.6" - "@babel/helper-validator-option": "npm:^7.24.6" + "@babel/compat-data": "npm:^7.24.7" + "@babel/helper-validator-option": "npm:^7.24.7" browserslist: "npm:^4.22.2" lru-cache: "npm:^5.1.1" semver: "npm:^6.3.1" - checksum: 10c0/4d41150086959f5f4d72d27bae29204192e943537ecb71df1711d1f5d8791358a44f3a5882ed3c8238ba0c874b0b55213af43767e14771765f13b8d15b262432 + checksum: 10c0/1d580a9bcacefe65e6bf02ba1dafd7ab278269fef45b5e281d8354d95c53031e019890464e7f9351898c01502dd2e633184eb0bcda49ed2ecd538675ce310f51 languageName: node linkType: hard @@ -186,10 +186,12 @@ __metadata: languageName: node linkType: hard -"@babel/helper-environment-visitor@npm:^7.24.6": - version: 7.24.6 - resolution: "@babel/helper-environment-visitor@npm:7.24.6" - checksum: 10c0/fdcd18ac505ed71f40c05cc992b648a4495b0aa5310a774492a0f74d8dcf3579691102f516561a651d3de6c3a44fe64bfb3049d11c14c5857634ef1823ea409a +"@babel/helper-environment-visitor@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/helper-environment-visitor@npm:7.24.7" + dependencies: + "@babel/types": "npm:^7.24.7" + checksum: 10c0/36ece78882b5960e2d26abf13cf15ff5689bf7c325b10a2895a74a499e712de0d305f8d78bb382dd3c05cfba7e47ec98fe28aab5674243e0625cd38438dd0b2d languageName: node linkType: hard @@ -203,13 +205,13 @@ __metadata: languageName: node linkType: hard -"@babel/helper-function-name@npm:^7.24.6": - version: 7.24.6 - resolution: "@babel/helper-function-name@npm:7.24.6" +"@babel/helper-function-name@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/helper-function-name@npm:7.24.7" dependencies: - "@babel/template": "npm:^7.24.6" - "@babel/types": "npm:^7.24.6" - checksum: 10c0/5ba2f8db789b3f5a2b2239300a217aa212e303cd7bfad9c8b90563807f49215e8c679e8f8f177b6aaca2038038e29bc702b83839e1f7b4896d79c44a75cac97a + "@babel/template": "npm:^7.24.7" + "@babel/types": "npm:^7.24.7" + checksum: 10c0/e5e41e6cf86bd0f8bf272cbb6e7c5ee0f3e9660414174435a46653efba4f2479ce03ce04abff2aa2ef9359cf057c79c06cb7b134a565ad9c0e8a50dcdc3b43c4 languageName: node linkType: hard @@ -222,12 +224,12 @@ __metadata: languageName: node linkType: hard -"@babel/helper-hoist-variables@npm:^7.24.6": - version: 7.24.6 - resolution: "@babel/helper-hoist-variables@npm:7.24.6" +"@babel/helper-hoist-variables@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/helper-hoist-variables@npm:7.24.7" dependencies: - "@babel/types": "npm:^7.24.6" - checksum: 10c0/e10ec6b864aaa419ec4934f5fcb5d0cfcc9d0657584a1b6c3c42ada949d44ca6bffcdab433a90ada4396c747e551cca31ba0e565ea005ab3f50964e3817bf6cf + "@babel/types": "npm:^7.24.7" + checksum: 10c0/19ee37563bbd1219f9d98991ad0e9abef77803ee5945fd85aa7aa62a67c69efca9a801696a1b58dda27f211e878b3327789e6fd2a6f6c725ccefe36774b5ce95 languageName: node linkType: hard @@ -240,12 +242,13 @@ __metadata: languageName: node linkType: hard -"@babel/helper-module-imports@npm:^7.24.6": - version: 7.24.6 - resolution: "@babel/helper-module-imports@npm:7.24.6" +"@babel/helper-module-imports@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/helper-module-imports@npm:7.24.7" dependencies: - "@babel/types": "npm:^7.24.6" - checksum: 10c0/e0db3fbfcd963d138f0792ff626f940a576fcf212d02b8fe6478dccf3421bd1c2a76f8e69c7450c049985e7b63b30be309a24eeeb6ad7c2137a31b676a095a84 + "@babel/traverse": "npm:^7.24.7" + "@babel/types": "npm:^7.24.7" + checksum: 10c0/97c57db6c3eeaea31564286e328a9fb52b0313c5cfcc7eee4bc226aebcf0418ea5b6fe78673c0e4a774512ec6c86e309d0f326e99d2b37bfc16a25a032498af0 languageName: node linkType: hard @@ -264,18 +267,18 @@ __metadata: languageName: node linkType: hard -"@babel/helper-module-transforms@npm:^7.24.6": - version: 7.24.6 - resolution: "@babel/helper-module-transforms@npm:7.24.6" +"@babel/helper-module-transforms@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/helper-module-transforms@npm:7.24.7" dependencies: - "@babel/helper-environment-visitor": "npm:^7.24.6" - "@babel/helper-module-imports": "npm:^7.24.6" - "@babel/helper-simple-access": "npm:^7.24.6" - "@babel/helper-split-export-declaration": "npm:^7.24.6" - "@babel/helper-validator-identifier": "npm:^7.24.6" + "@babel/helper-environment-visitor": "npm:^7.24.7" + "@babel/helper-module-imports": "npm:^7.24.7" + "@babel/helper-simple-access": "npm:^7.24.7" + "@babel/helper-split-export-declaration": "npm:^7.24.7" + "@babel/helper-validator-identifier": "npm:^7.24.7" peerDependencies: "@babel/core": ^7.0.0 - checksum: 10c0/9e2e3d0ddb397b36b9e8c7d94e175a36be8cb888ef370cefef2cdfd53ae1f87d567b268bd90ed9a6c706485a8de3da19cac577657613e9cd17210b91cbdfb00b + checksum: 10c0/4f311755fcc3b4cbdb689386309cdb349cf0575a938f0b9ab5d678e1a81bbb265aa34ad93174838245f2ac7ff6d5ddbd0104638a75e4e961958ed514355687b6 languageName: node linkType: hard @@ -295,12 +298,13 @@ __metadata: languageName: node linkType: hard -"@babel/helper-simple-access@npm:^7.24.6": - version: 7.24.6 - resolution: "@babel/helper-simple-access@npm:7.24.6" +"@babel/helper-simple-access@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/helper-simple-access@npm:7.24.7" dependencies: - "@babel/types": "npm:^7.24.6" - checksum: 10c0/b17e404dd6c9787fc7d558aea5222471a77e29596705f0d10b4c2a58b9d71ff7eae915094204848cc1af99b771553caa69337a768b9abdd82b54a0050ba83eb9 + "@babel/traverse": "npm:^7.24.7" + "@babel/types": "npm:^7.24.7" + checksum: 10c0/7230e419d59a85f93153415100a5faff23c133d7442c19e0cd070da1784d13cd29096ee6c5a5761065c44e8164f9f80e3a518c41a0256df39e38f7ad6744fed7 languageName: node linkType: hard @@ -313,12 +317,12 @@ __metadata: languageName: node linkType: hard -"@babel/helper-split-export-declaration@npm:^7.24.6": - version: 7.24.6 - resolution: "@babel/helper-split-export-declaration@npm:7.24.6" +"@babel/helper-split-export-declaration@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/helper-split-export-declaration@npm:7.24.7" dependencies: - "@babel/types": "npm:^7.24.6" - checksum: 10c0/53a5dd8691fdffc89cc7fcf5aed0ad1d8bc39796a5782a3d170dcbf249eb5c15cc8a290e8d09615711d18798ad04a7d0694ab5195d35fa651abbc1b9c885d6a8 + "@babel/types": "npm:^7.24.7" + checksum: 10c0/0254577d7086bf09b01bbde98f731d4fcf4b7c3fa9634fdb87929801307c1f6202a1352e3faa5492450fa8da4420542d44de604daf540704ff349594a78184f6 languageName: node linkType: hard @@ -329,10 +333,10 @@ __metadata: languageName: node linkType: hard -"@babel/helper-string-parser@npm:^7.24.6": - version: 7.24.6 - resolution: "@babel/helper-string-parser@npm:7.24.6" - checksum: 10c0/95115bf676e92c4e99166395649108d97447e6cabef1fabaec8cdbc53a43f27b5df2268ff6534439d405bc1bd06685b163eb3b470455bd49f69159dada414145 +"@babel/helper-string-parser@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/helper-string-parser@npm:7.24.7" + checksum: 10c0/47840c7004e735f3dc93939c77b099bb41a64bf3dda0cae62f60e6f74a5ff80b63e9b7cf77b5ec25a324516381fc994e1f62f922533236a8e3a6af57decb5e1e languageName: node linkType: hard @@ -343,10 +347,10 @@ __metadata: languageName: node linkType: hard -"@babel/helper-validator-identifier@npm:^7.24.6": - version: 7.24.6 - resolution: "@babel/helper-validator-identifier@npm:7.24.6" - checksum: 10c0/d29d2e3fca66c31867a009014169b93f7bc21c8fc1dd7d0b9d85d7a4000670526ff2222d966febb75a6e12f9859a31d1e75b558984e28ecb69651314dd0a6fd1 +"@babel/helper-validator-identifier@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/helper-validator-identifier@npm:7.24.7" + checksum: 10c0/87ad608694c9477814093ed5b5c080c2e06d44cb1924ae8320474a74415241223cc2a725eea2640dd783ff1e3390e5f95eede978bc540e870053152e58f1d651 languageName: node linkType: hard @@ -357,10 +361,10 @@ __metadata: languageName: node linkType: hard -"@babel/helper-validator-option@npm:^7.24.6": - version: 7.24.6 - resolution: "@babel/helper-validator-option@npm:7.24.6" - checksum: 10c0/787268dff5cf77f3b704454b96ab7b58aa4f43b2808247e51859a103a1c28a9c252100f830433f4b37a73f4a61ba745bbeef4cdccbab48c1e9adf037f4ca3491 +"@babel/helper-validator-option@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/helper-validator-option@npm:7.24.7" + checksum: 10c0/21aea2b7bc5cc8ddfb828741d5c8116a84cbc35b4a3184ec53124f08e09746f1f67a6f9217850188995ca86059a7942e36d8965a6730784901def777b7e8a436 languageName: node linkType: hard @@ -375,13 +379,13 @@ __metadata: languageName: node linkType: hard -"@babel/helpers@npm:^7.24.6": - version: 7.24.6 - resolution: "@babel/helpers@npm:7.24.6" +"@babel/helpers@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/helpers@npm:7.24.7" dependencies: - "@babel/template": "npm:^7.24.6" - "@babel/types": "npm:^7.24.6" - checksum: 10c0/e5b5c0919fd6fa56ae11c15a72962d8de0ac19db524849554af28cf08ac32f9ae5aee49a43146eb150f54418cefb8e890fa2b2f33d029434dc7777dbcdfd5bac + "@babel/template": "npm:^7.24.7" + "@babel/types": "npm:^7.24.7" + checksum: 10c0/aa8e230f6668773e17e141dbcab63e935c514b4b0bf1fed04d2eaefda17df68e16b61a56573f7f1d4d1e605ce6cc162b5f7e9fdf159fde1fd9b77c920ae47d27 languageName: node linkType: hard @@ -397,15 +401,15 @@ __metadata: languageName: node linkType: hard -"@babel/highlight@npm:^7.24.6": - version: 7.24.6 - resolution: "@babel/highlight@npm:7.24.6" +"@babel/highlight@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/highlight@npm:7.24.7" dependencies: - "@babel/helper-validator-identifier": "npm:^7.24.6" + "@babel/helper-validator-identifier": "npm:^7.24.7" chalk: "npm:^2.4.2" js-tokens: "npm:^4.0.0" picocolors: "npm:^1.0.0" - checksum: 10c0/5bbc31695e5d44e97feb267f7aaf4c52908560d184ffeb2e2e57aae058d40125592931883889413e19def3326895ddb41ff45e090fa90b459d8c294b4ffc238c + checksum: 10c0/674334c571d2bb9d1c89bdd87566383f59231e16bcdcf5bb7835babdf03c9ae585ca0887a7b25bdf78f303984af028df52831c7989fecebb5101cc132da9393a languageName: node linkType: hard @@ -418,12 +422,12 @@ __metadata: languageName: node linkType: hard -"@babel/parser@npm:^7.24.6": - version: 7.24.6 - resolution: "@babel/parser@npm:7.24.6" +"@babel/parser@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/parser@npm:7.24.7" bin: parser: ./bin/babel-parser.js - checksum: 10c0/cbef70923078a20fe163b03f4a6482be65ed99d409a57f3091a23ce3a575ee75716c30e7ea9f40b692ac5660f34055f4cbeb66a354fad15a6cf1fca35c3496c5 + checksum: 10c0/8b244756872185a1c6f14b979b3535e682ff08cb5a2a5fd97cc36c017c7ef431ba76439e95e419d43000c5b07720495b00cf29a7f0d9a483643d08802b58819b languageName: node linkType: hard @@ -484,14 +488,14 @@ __metadata: languageName: node linkType: hard -"@babel/template@npm:^7.24.6": - version: 7.24.6 - resolution: "@babel/template@npm:7.24.6" +"@babel/template@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/template@npm:7.24.7" dependencies: - "@babel/code-frame": "npm:^7.24.6" - "@babel/parser": "npm:^7.24.6" - "@babel/types": "npm:^7.24.6" - checksum: 10c0/a4d5805770de908b445f7cdcebfcb6eaa07b1ec9c7b78fd3f375a911b1522c249bddae6b96bc4aac24247cc603e3e6cffcf2fe50b4c929dfeb22de289b517525 + "@babel/code-frame": "npm:^7.24.7" + "@babel/parser": "npm:^7.24.7" + "@babel/types": "npm:^7.24.7" + checksum: 10c0/95b0b3ee80fcef685b7f4426f5713a855ea2cd5ac4da829b213f8fb5afe48a2a14683c2ea04d446dbc7f711c33c5cd4a965ef34dcbe5bc387c9e966b67877ae3 languageName: node linkType: hard @@ -531,21 +535,21 @@ __metadata: languageName: node linkType: hard -"@babel/traverse@npm:^7.24.6": - version: 7.24.6 - resolution: "@babel/traverse@npm:7.24.6" +"@babel/traverse@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/traverse@npm:7.24.7" dependencies: - "@babel/code-frame": "npm:^7.24.6" - "@babel/generator": "npm:^7.24.6" - "@babel/helper-environment-visitor": "npm:^7.24.6" - "@babel/helper-function-name": "npm:^7.24.6" - "@babel/helper-hoist-variables": "npm:^7.24.6" - "@babel/helper-split-export-declaration": "npm:^7.24.6" - "@babel/parser": "npm:^7.24.6" - "@babel/types": "npm:^7.24.6" + "@babel/code-frame": "npm:^7.24.7" + "@babel/generator": "npm:^7.24.7" + "@babel/helper-environment-visitor": "npm:^7.24.7" + "@babel/helper-function-name": "npm:^7.24.7" + "@babel/helper-hoist-variables": "npm:^7.24.7" + "@babel/helper-split-export-declaration": "npm:^7.24.7" + "@babel/parser": "npm:^7.24.7" + "@babel/types": "npm:^7.24.7" debug: "npm:^4.3.1" globals: "npm:^11.1.0" - checksum: 10c0/39027d5fc7a241c6b71bb5872c2bdcec53743cd7ef3c151bbe6fd7cf874d15f4bc09e5d7e19e2f534b0eb2c115f5368553885fa4253aa1bc9441c6e5bf9efdaf + checksum: 10c0/a5135e589c3f1972b8877805f50a084a04865ccb1d68e5e1f3b94a8841b3485da4142e33413d8fd76bc0e6444531d3adf1f59f359c11ffac452b743d835068ab languageName: node linkType: hard @@ -570,14 +574,14 @@ __metadata: languageName: node linkType: hard -"@babel/types@npm:^7.24.6": - version: 7.24.6 - resolution: "@babel/types@npm:7.24.6" +"@babel/types@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/types@npm:7.24.7" dependencies: - "@babel/helper-string-parser": "npm:^7.24.6" - "@babel/helper-validator-identifier": "npm:^7.24.6" + "@babel/helper-string-parser": "npm:^7.24.7" + "@babel/helper-validator-identifier": "npm:^7.24.7" to-fast-properties: "npm:^2.0.0" - checksum: 10c0/1d94d92d97ef49030ad7f9e14cfccfeb70b1706dabcaa69037e659ec9d2c3178fb005d2088cce40d88dfc1306153d9157fe038a79ea2be92e5e6b99a59ef80cc + checksum: 10c0/d9ecbfc3eb2b05fb1e6eeea546836ac30d990f395ef3fe3f75ced777a222c3cfc4489492f72e0ce3d9a5a28860a1ce5f81e66b88cf5088909068b3ff4fab72c1 languageName: node linkType: hard @@ -1738,12 +1742,12 @@ __metadata: languageName: node linkType: hard -"@types/node@npm:^20.14.1": - version: 20.14.1 - resolution: "@types/node@npm:20.14.1" +"@types/node@npm:^20.14.2": + version: 20.14.2 + resolution: "@types/node@npm:20.14.2" dependencies: undici-types: "npm:~5.26.4" - checksum: 10c0/12b7879047f50cc217bbea3add7c45e542070f6e9fb2092be97542152b7022512bcb2bf848d04f77e295c4c8699acd484e79a4a4dbe9bcfa4e89dd543d530611 + checksum: 10c0/2d86e5f2227aaa42212e82ea0affe72799111b888ff900916376450b02b09b963ca888b20d9c332d8d2b833ed4781987867a38eaa2e4863fa8439071468b0a6f languageName: node linkType: hard @@ -1962,7 +1966,7 @@ __metadata: dependencies: "@alova/adapter-xhr": "npm:^1.0.6" "@alova/scene-react": "npm:^1.5.0" - "@babel/core": "npm:^7.24.6" + "@babel/core": "npm:^7.24.7" "@emotion/react": "npm:^11.11.4" "@emotion/styled": "npm:^11.11.5" "@eslint/js": "npm:^9.4.0" @@ -1974,7 +1978,7 @@ __metadata: "@trivago/prettier-plugin-sort-imports": "npm:^4.3.0" "@types/babel__core": "npm:^7" "@types/lodash-es": "npm:^4.17.12" - "@types/node": "npm:^20.14.1" + "@types/node": "npm:^20.14.2" "@types/react": "npm:^18.3.3" "@types/react-dom": "npm:^18.3.0" "@types/react-router-dom": "npm:^5.3.3" @@ -1988,7 +1992,7 @@ __metadata: lodash-es: "npm:^4.17.21" mime-types: "npm:^2.1.35" preact: "npm:^10.22.0" - prettier: "npm:^3.3.0" + prettier: "npm:^3.3.1" react: "npm:latest" react-dom: "npm:latest" react-dropzone: "npm:^14.2.3" @@ -1996,11 +2000,11 @@ __metadata: react-router-dom: "npm:^6.23.1" react-toastify: "npm:^10.0.5" rollup-plugin-visualizer: "npm:^5.12.0" - terser: "npm:^5.31.0" + terser: "npm:^5.31.1" typesafe-i18n: "npm:^5.26.2" typescript: "npm:^5.4.5" typescript-eslint: "npm:^7.12.0" - vite: "npm:^5.2.12" + vite: "npm:^5.2.13" vite-plugin-imagemin: "npm:^0.6.1" vite-tsconfig-paths: "npm:^4.3.2" languageName: unknown @@ -5932,12 +5936,12 @@ __metadata: languageName: node linkType: hard -"prettier@npm:^3.3.0": - version: 3.3.0 - resolution: "prettier@npm:3.3.0" +"prettier@npm:^3.3.1": + version: 3.3.1 + resolution: "prettier@npm:3.3.1" bin: prettier: bin/prettier.cjs - checksum: 10c0/d033c356320aa2e468bf29c931b094ac730d2f4defd5eb2989d8589313dec901d2fc866e3788f3d161e420b142ea4ec3dda535dbe0169ef4d0026397a68ba9cf + checksum: 10c0/c25a709c9f0be670dc6bcb190b622347e1dbeb6c3e7df8b0711724cb64d8647c60b839937a4df4df18e9cfb556c2b08ca9d24d9645eb5488a7fc032a2c4d5cb3 languageName: node linkType: hard @@ -6968,9 +6972,9 @@ __metadata: languageName: node linkType: hard -"terser@npm:^5.31.0": - version: 5.31.0 - resolution: "terser@npm:5.31.0" +"terser@npm:^5.31.1": + version: 5.31.1 + resolution: "terser@npm:5.31.1" dependencies: "@jridgewell/source-map": "npm:^0.3.3" acorn: "npm:^8.8.2" @@ -6978,7 +6982,7 @@ __metadata: source-map-support: "npm:~0.5.20" bin: terser: bin/terser - checksum: 10c0/cb127a579b03fb9dcee0d293ff24814deedcd430f447933b618e8593b7454f615b5c8493c68e86a4b0188769d5ea2af5251b5d507edb208114f7e8aebdc7c850 + checksum: 10c0/4d49a58f64c11f3742e779a0a03aff69972ca5739decb361d909d22c8f3f7d8e2ec982a928d987d56737ad50229e8ab3f62d8ba993e4b5f360a53ed487d3c06c languageName: node linkType: hard @@ -7318,9 +7322,9 @@ __metadata: languageName: node linkType: hard -"vite@npm:^5.2.12": - version: 5.2.12 - resolution: "vite@npm:5.2.12" +"vite@npm:^5.2.13": + version: 5.2.13 + resolution: "vite@npm:5.2.13" dependencies: esbuild: "npm:^0.20.1" fsevents: "npm:~2.3.3" @@ -7354,7 +7358,7 @@ __metadata: optional: true bin: vite: bin/vite.js - checksum: 10c0/f03fdfc320adea3397df3e327029fd875f8220779f679ab183a3a994e8788b4ce531fee28f830361fb274f3cf08ed9adb9429496ecefdc3faf535b38da7ea8b1 + checksum: 10c0/f7a99da71884e69cc581dcfb43d73c8d56d73b9668d6980131603c544d6323c6003a20f376531dc0cfcf36bf5009bc465f89e6c5f8bd9d22868987aba4e4af1b languageName: node linkType: hard diff --git a/lib/AsyncTCP/src/AsyncTCP.h b/lib/AsyncTCP/src/AsyncTCP.h index 3d9c61f72..b44a6ced7 100644 --- a/lib/AsyncTCP/src/AsyncTCP.h +++ b/lib/AsyncTCP/src/AsyncTCP.h @@ -48,7 +48,7 @@ extern "C" { //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-ESP32 +// 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 @@ -56,13 +56,13 @@ extern "C" { #define CONFIG_ASYNC_TCP_TASK_PRIORITY 5 #endif -// EMS-ESP32: stack usage measured: ESP32: ~2.3K, ESP32S3: ~3.5k +// EMS-ESP: stack usage measured: ESP32: ~2.3K, ESP32S3: ~3.5k #ifndef CONFIG_ASYNC_TCP_STACK_SIZE #define CONFIG_ASYNC_TCP_STACK_SIZE 5120 #endif -// EMS-ESP32: maybe enlarge queue to 64 or 128 see https://github.com/emsesp/EMS-ESP32/issues/177 +// EMS-ESP: maybe enlarge queue to 64 or 128 see https://github.com/emsesp/EMS-ESP32/issues/177 #ifndef CONFIG_ASYNC_TCP_QUEUE #define CONFIG_ASYNC_TCP_QUEUE 32 #endif diff --git a/lib/ESPAsyncWebServer/src/AsyncWebSocket.cpp b/lib/ESPAsyncWebServer/src/AsyncWebSocket.cpp index 182ea0536..3bae20280 100644 --- a/lib/ESPAsyncWebServer/src/AsyncWebSocket.cpp +++ b/lib/ESPAsyncWebServer/src/AsyncWebSocket.cpp @@ -26,99 +26,105 @@ #include #ifndef ESP8266 -#include "mbedtls/sha1.h" +#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 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; +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; + 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; - } + 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); + 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); - // Serial.println("SF 4"); - return 0; - } - free(buf); - if(len){ - if(len && mask){ - size_t i; - for(i=0;iadd((const char *)data, len) != len) { + //os_printf("error adding %lu data bytes\n", len); + // Serial.println("SF 5"); + return 0; + } } - 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; } - } - if(!client->send()){ - //os_printf("error sending frame: %lu\n", headLen+len); - // Serial.println("SF 6"); - return 0; - } - // Serial.println("SF"); - return len; + // Serial.println("SF"); + return len; } @@ -128,13 +134,11 @@ size_t webSocketSendFrame(AsyncClient *client, bool final, uint8_t opcode, bool */ AsyncWebSocketMessageBuffer::AsyncWebSocketMessageBuffer() - : _buffer(std::make_shared>(0)) -{ + : _buffer(std::make_shared>(0)) { } -AsyncWebSocketMessageBuffer::AsyncWebSocketMessageBuffer(uint8_t* data, size_t size) - : _buffer(std::make_shared>(size)) -{ +AsyncWebSocketMessageBuffer::AsyncWebSocketMessageBuffer(uint8_t * data, size_t size) + : _buffer(std::make_shared>(size)) { if (_buffer->capacity() < size) { _buffer.reset(); _buffer = std::make_shared>(0); @@ -144,21 +148,18 @@ AsyncWebSocketMessageBuffer::AsyncWebSocketMessageBuffer(uint8_t* data, size_t s } AsyncWebSocketMessageBuffer::AsyncWebSocketMessageBuffer(size_t size) - : _buffer(std::make_shared>(size)) -{ + : _buffer(std::make_shared>(size)) { if (_buffer->capacity() < size) { _buffer.reset(); _buffer = std::make_shared>(0); } } -AsyncWebSocketMessageBuffer::~AsyncWebSocketMessageBuffer() -{ +AsyncWebSocketMessageBuffer::~AsyncWebSocketMessageBuffer() { _buffer.reset(); } -bool AsyncWebSocketMessageBuffer::reserve(size_t size) -{ +bool AsyncWebSocketMessageBuffer::reserve(size_t size) { if (_buffer->capacity() >= size) return true; _buffer->reserve(size); @@ -170,48 +171,50 @@ bool AsyncWebSocketMessageBuffer::reserve(size_t size) */ class AsyncWebSocketControl { -private: - uint8_t _opcode; - uint8_t *_data; - size_t _len; - bool _mask; - bool _finished; + 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) - { + 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) { if (_len > 125) _len = 125; - _data = (uint8_t*)malloc(_len); + _data = (uint8_t *)malloc(_len); - if(_data == NULL) + if (_data == NULL) _len = 0; else memcpy(_data, data, len); - } - else + } else _data = NULL; } - virtual ~AsyncWebSocketControl() - { + 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){ + 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); } @@ -223,40 +226,34 @@ public: */ -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} -{ -} +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 AsyncWebSocketMessage::ack(size_t len, uint32_t time) { (void)time; _acked += len; - if (_sent >= _WSbuffer->size() && _acked >= _ack) - { + if (_sent >= _WSbuffer->size() && _acked >= _ack) { _status = WS_MSG_SENT; } //ets_printf("A: %u\n", len); } -size_t AsyncWebSocketMessage::send(AsyncClient *client) -{ +size_t AsyncWebSocketMessage::send(AsyncClient * client) { if (_status != WS_MSG_SENDING) return 0; - if (_acked < _ack){ + if (_acked < _ack) { return 0; } - if (_sent == _WSbuffer->size()) - { - if(_acked == _ack) + if (_sent == _WSbuffer->size()) { + if (_acked == _ack) _status = WS_MSG_SENT; return 0; } - if (_sent > _WSbuffer->size()) - { + if (_sent > _WSbuffer->size()) { _status = WS_MSG_ERROR; //ets_printf("E: %u > %u\n", _sent, _WSbuffer->length()); return 0; @@ -270,17 +267,17 @@ size_t AsyncWebSocketMessage::send(AsyncClient *client) } _sent += toSend; - _ack += toSend + ((toSend < 126)?2:4) + (_mask * 4); + _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; + 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){ + _status = WS_MSG_SENDING; + if (toSend && sent != toSend) { //ets_printf("E: %u != %u\n", toSend, sent); _sent -= (toSend - sent); _ack -= (toSend - sent); @@ -293,33 +290,61 @@ size_t AsyncWebSocketMessage::send(AsyncClient *client) /* * Async WebSocket Client */ - const char * AWSC_PING_PAYLOAD = "ESPAsyncWebServer-PING"; - const size_t AWSC_PING_PAYLOAD_LEN = 22; +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; +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); + _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)); + memset(&_pinfo, 0, sizeof(_pinfo)); } -AsyncWebSocketClient::~AsyncWebSocketClient() -{ +AsyncWebSocketClient::~AsyncWebSocketClient() { { AsyncWebLockGuard l(_lock); @@ -329,33 +354,33 @@ AsyncWebSocketClient::~AsyncWebSocketClient() _server->_handleEvent(this, WS_EVT_DISCONNECT, NULL, NULL, 0); } -void AsyncWebSocketClient::_clearQueue() -{ +void AsyncWebSocketClient::_clearQueue() { while (!_messageQueue.empty() && _messageQueue.front().finished()) - _messageQueue.pop_front(); + _messageQueue.pop_front(); } -void AsyncWebSocketClient::_onAck(size_t len, uint32_t time){ +void AsyncWebSocketClient::_onAck(size_t len, uint32_t time) { _lastMessageTime = millis(); AsyncWebLockGuard l(_lock); if (!_controlQueue.empty()) { - auto &head = _controlQueue.front(); - if (head.finished()){ + auto & head = _controlQueue.front(); + if (head.finished()) { len -= head.len(); - if (_status == WS_DISCONNECTING && head.opcode() == WS_DISCONNECT){ + if (_status == WS_DISCONNECTING && head.opcode() == WS_DISCONNECT) { _controlQueue.pop_front(); _status = WS_DISCONNECTED; l.unlock(); - if (_client) _client->close(true); + if (_client) + _client->close(true); return; } _controlQueue.pop_front(); } } - if(len && !_messageQueue.empty()){ + if (len && !_messageQueue.empty()) { _messageQueue.front().ack(len, time); } @@ -364,26 +389,21 @@ void AsyncWebSocketClient::_onAck(size_t len, uint32_t time){ _runQueue(); } -void AsyncWebSocketClient::_onPoll() -{ +void AsyncWebSocketClient::_onPoll() { if (!_client) return; AsyncWebLockGuard l(_lock); - if (_client->canSend() && (!_controlQueue.empty() || !_messageQueue.empty())) - { + if (_client->canSend() && (!_controlQueue.empty() || !_messageQueue.empty())) { l.unlock(); _runQueue(); - } - else if (_keepAlivePeriod > 0 && (millis() - _lastMessageTime) >= _keepAlivePeriod && (_controlQueue.empty() && _messageQueue.empty())) - { + } 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() -{ +void AsyncWebSocketClient::_runQueue() { if (!_client) return; @@ -391,20 +411,17 @@ void AsyncWebSocketClient::_runQueue() _clearQueue(); - if (!_controlQueue.empty() && (_messageQueue.empty() || _messageQueue.front().betweenFrames()) && webSocketSendFrameWindow(_client) > (size_t)(_controlQueue.front().len() - 1)) - { + 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)) - { + } else if (!_messageQueue.empty() && _messageQueue.front().betweenFrames() && webSocketSendFrameWindow(_client)) { //l.unlock(); _messageQueue.front().send(_client); } } -bool AsyncWebSocketClient::queueIsFull() const -{ +bool AsyncWebSocketClient::queueIsFull() const { size_t size; { AsyncWebLockGuard l(_lock); @@ -413,15 +430,13 @@ bool AsyncWebSocketClient::queueIsFull() const return (size >= WS_MAX_QUEUED_MESSAGES) || (_status != WS_CONNECTED); } -size_t AsyncWebSocketClient::queueLen() const -{ +size_t AsyncWebSocketClient::queueLen() const { AsyncWebLockGuard l(_lock); return _messageQueue.size() + _controlQueue.size(); } -bool AsyncWebSocketClient::canSend() const -{ +bool AsyncWebSocketClient::canSend() const { size_t size; { AsyncWebLockGuard l(_lock); @@ -430,8 +445,7 @@ bool AsyncWebSocketClient::canSend() const return size < WS_MAX_QUEUED_MESSAGES; } -void AsyncWebSocketClient::_queueControl(uint8_t opcode, const uint8_t *data, size_t len, bool mask) -{ +void AsyncWebSocketClient::_queueControl(uint8_t opcode, const uint8_t * data, size_t len, bool mask) { if (!_client) return; @@ -444,9 +458,8 @@ void AsyncWebSocketClient::_queueControl(uint8_t opcode, const uint8_t *data, si _runQueue(); } -void AsyncWebSocketClient::_queueMessage(std::shared_ptr> buffer, uint8_t opcode, bool mask) -{ - if(_status != WS_CONNECTED) +void AsyncWebSocketClient::_queueMessage(std::shared_ptr> buffer, uint8_t opcode, bool mask) { + if (_status != WS_CONNECTED) return; if (!_client) @@ -457,16 +470,26 @@ void AsyncWebSocketClient::_queueMessage(std::shared_ptr> b { AsyncWebLockGuard l(_lock); - if (_messageQueue.size() >= WS_MAX_QUEUED_MESSAGES) - { + if (_messageQueue.size() >= WS_MAX_QUEUED_MESSAGES) { l.unlock(); - //ets_printf("AsyncWebSocketClient::_queueMessage: Too many messages queued, closing connection\n"); - _status = WS_DISCONNECTED; - if (_client) _client->close(true); + 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 - { + } else { _messageQueue.emplace_back(buffer, opcode, mask); } } @@ -475,29 +498,26 @@ void AsyncWebSocketClient::_queueMessage(std::shared_ptr> b _runQueue(); } -void AsyncWebSocketClient::close(uint16_t code, const char * message) -{ - if(_status != WS_CONNECTED) +void AsyncWebSocketClient::close(uint16_t code, const char * message) { + if (_status != WS_CONNECTED) return; - if(code) - { + if (code) { uint8_t packetLen = 2; - if (message != NULL) - { + if (message != NULL) { size_t mlen = strlen(message); - if(mlen > 123) mlen = 123; + if (mlen > 123) + mlen = 123; packetLen += mlen; } - char * buf = (char*)malloc(packetLen); - if (buf != NULL) - { + 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); + if (message != NULL) { + memcpy(buf + 2, message, packetLen - 2); } - _queueControl(WS_DISCONNECT, (uint8_t*)buf, packetLen); + _queueControl(WS_DISCONNECT, (uint8_t *)buf, packetLen); free(buf); return; } @@ -505,245 +525,233 @@ void AsyncWebSocketClient::close(uint16_t code, const char * message) _queueControl(WS_DISCONNECT); } -void AsyncWebSocketClient::ping(const uint8_t *data, size_t len) -{ +void AsyncWebSocketClient::ping(const uint8_t * data, size_t len) { if (_status == WS_CONNECTED) _queueControl(WS_PING, data, len); } -void AsyncWebSocketClient::_onError(int8_t) -{ +void AsyncWebSocketClient::_onError(int8_t) { //Serial.println("onErr"); } -void AsyncWebSocketClient::_onTimeout(uint32_t time) -{ +void AsyncWebSocketClient::_onTimeout(uint32_t time) { // Serial.println("onTime"); (void)time; _client->close(true); } -void AsyncWebSocketClient::_onDisconnect() -{ +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; - } +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 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)); - } + 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(_status == WS_DISCONNECTING){ - _status = WS_DISCONNECTED; - _client->close(true); + + 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 { - _status = WS_DISCONNECTING; - _client->ackLater(); - _queueControl(WS_DISCONNECT, data, datalen); + //os_printf("frame error: len: %u, index: %llu, total: %llu\n", datalen, _pinfo.index, _pinfo.len); + //what should we do? + break; } - } 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; } - - // 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; - } +size_t AsyncWebSocketClient::printf(const char * format, ...) { + va_list arg; va_start(arg, format); - vsnprintf(buffer, len + 1, format, arg); + 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); - } - text(buffer, len); - if (buffer != temp) { - delete[] buffer; - } - delete[] temp; - return len; + + 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; - } +size_t AsyncWebSocketClient::printf_P(PGM_P formatP, ...) { + va_list arg; va_start(arg, formatP); - vsnprintf_P(buffer, len + 1, formatP, arg); + 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); - } - text(buffer, len); - if (buffer != temp) { - delete[] buffer; - } - delete[] temp; - return len; + + 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) -{ +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) -{ +void AsyncWebSocketClient::text(AsyncWebSocketMessageBuffer * buffer) { if (buffer) { text(std::move(buffer->_buffer)); delete buffer; } } -void AsyncWebSocketClient::text(std::shared_ptr> buffer) -{ +void AsyncWebSocketClient::text(std::shared_ptr> buffer) { _queueMessage(buffer); } -void AsyncWebSocketClient::text(const uint8_t *message, size_t len) -{ +void AsyncWebSocketClient::text(const uint8_t * message, size_t len) { text(makeSharedBuffer(message, len)); } -void AsyncWebSocketClient::text(const char *message, size_t len) -{ +void AsyncWebSocketClient::text(const char * message, size_t len) { text((const uint8_t *)message, len); } -void AsyncWebSocketClient::text(const char *message) -{ +void AsyncWebSocketClient::text(const char * message) { text(message, strlen(message)); } -void AsyncWebSocketClient::text(const String &message) -{ +void AsyncWebSocketClient::text(const String & message) { text(message.c_str(), message.length()); } -void AsyncWebSocketClient::text(const __FlashStringHelper *data) -{ +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; + while (1) { + if (pgm_read_byte(p + n) == 0) + break; + n += 1; } - char * message = (char*) malloc(n+1); - if(message) - { + char * message = (char *)malloc(n + 1); + if (message) { memcpy_P(message, p, n); message[n] = 0; text(message, n); @@ -751,43 +759,36 @@ void AsyncWebSocketClient::text(const __FlashStringHelper *data) } } -void AsyncWebSocketClient::binary(AsyncWebSocketMessageBuffer * buffer) -{ +void AsyncWebSocketClient::binary(AsyncWebSocketMessageBuffer * buffer) { if (buffer) { binary(std::move(buffer->_buffer)); delete buffer; } } -void AsyncWebSocketClient::binary(std::shared_ptr> buffer) -{ +void AsyncWebSocketClient::binary(std::shared_ptr> buffer) { _queueMessage(buffer, WS_BINARY); } -void AsyncWebSocketClient::binary(const uint8_t *message, size_t len) -{ +void AsyncWebSocketClient::binary(const uint8_t * message, size_t len) { binary(makeSharedBuffer(message, len)); } -void AsyncWebSocketClient::binary(const char *message, size_t len) -{ +void AsyncWebSocketClient::binary(const char * message, size_t len) { binary((const uint8_t *)message, len); } -void AsyncWebSocketClient::binary(const char *message) -{ +void AsyncWebSocketClient::binary(const char * message) { binary(message, strlen(message)); } -void AsyncWebSocketClient::binary(const String &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); +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); @@ -795,17 +796,15 @@ void AsyncWebSocketClient::binary(const __FlashStringHelper *data, size_t len) } } -IPAddress AsyncWebSocketClient::remoteIP() const -{ +IPAddress AsyncWebSocketClient::remoteIP() const { if (!_client) return IPAddress((uint32_t)0U); return _client->remoteIP(); } -uint16_t AsyncWebSocketClient::remotePort() const -{ - if(!_client) +uint16_t AsyncWebSocketClient::remotePort() const { + if (!_client) return 0; return _client->remotePort(); @@ -817,53 +816,45 @@ uint16_t AsyncWebSocketClient::remotePort() const * Async Web Socket - Each separate socket location */ -AsyncWebSocket::AsyncWebSocket(const String& url) - :_url(url) - ,_cNextId(1) - ,_enabled(true) -{ - _eventHandler = NULL; +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); - } +AsyncWebSocket::~AsyncWebSocket() { } -AsyncWebSocketClient *AsyncWebSocket::_newClient(AsyncWebServerRequest *request) -{ +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::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; }); +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; }); +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; }); +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; @@ -871,26 +862,22 @@ AsyncWebSocketClient * AsyncWebSocket::client(uint32_t id) } -void AsyncWebSocket::close(uint32_t id, uint16_t code, const char * message) -{ - if (AsyncWebSocketClient *c = client(id)) +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) +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) -{ +void AsyncWebSocket::cleanupClients(uint16_t maxClients) { if (count() > maxClients) _clients.front().close(); - for (auto iter = std::begin(_clients); iter != std::end(_clients);) - { + for (auto iter = std::begin(_clients); iter != std::end(_clients);) { if (iter->shouldBeDeleted()) iter = _clients.erase(iter); else @@ -898,186 +885,172 @@ void AsyncWebSocket::cleanupClients(uint16_t maxClients) } } -void AsyncWebSocket::ping(uint32_t id, const uint8_t *data, size_t len) -{ +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) +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) -{ +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) -{ +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) -{ +void AsyncWebSocket::text(uint32_t id, const char * message) { text(id, message, strlen(message)); } -void AsyncWebSocket::text(uint32_t id, const String &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) -{ +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) + while (true) { + if (pgm_read_byte(p + n) == 0) break; n += 1; } - char * message = (char*) malloc(n+1); - if (message) - { + char * message = (char *)malloc(n + 1); + if (message) { memcpy_P(message, p, n); message[n] = 0; text(id, message, n); free(message); } } - -void AsyncWebSocket::textAll(AsyncWebSocketMessageBuffer * buffer) -{ +void AsyncWebSocket::text(uint32_t id, AsyncWebSocketMessageBuffer * buffer) { if (buffer) { - textAll(std::move(buffer->_buffer)); - delete buffer; + text(id, 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::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) -{ + +void AsyncWebSocket::textAll(const uint8_t * message, size_t len) { textAll(makeSharedBuffer(message, len)); } -void AsyncWebSocket::textAll(const char * message, size_t len) -{ +void AsyncWebSocket::textAll(const char * message, size_t len) { textAll((const uint8_t *)message, len); } -void AsyncWebSocket::textAll(const char *message) -{ +void AsyncWebSocket::textAll(const char * message) { textAll(message, strlen(message)); } -void AsyncWebSocket::textAll(const String &message) -{ +void AsyncWebSocket::textAll(const String & message) { textAll(message.c_str(), message.length()); } -void AsyncWebSocket::textAll(const __FlashStringHelper *data) -{ +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; + while (1) { + if (pgm_read_byte(p + n) == 0) + break; + n += 1; } - char *message = (char*)malloc(n+1); - if(message) - { + 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::binary(uint32_t id, const uint8_t *message, size_t len) -{ - if (AsyncWebSocketClient *c = client(id)) +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) -{ +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) -{ +void AsyncWebSocket::binary(uint32_t id, const char * message) { binary(id, message, strlen(message)); } -void AsyncWebSocket::binary(uint32_t id, const String &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) - { +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::binaryAll(AsyncWebSocketMessageBuffer * buffer) -{ +void AsyncWebSocket::binary(uint32_t id, AsyncWebSocketMessageBuffer * buffer) { if (buffer) { - binaryAll(std::move(buffer->_buffer)); + binary(id, 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); +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) -{ + +void AsyncWebSocket::binaryAll(const uint8_t * message, size_t len) { binaryAll(makeSharedBuffer(message, len)); } - -void AsyncWebSocket::binaryAll(const char *message, size_t len) -{ +void AsyncWebSocket::binaryAll(const char * message, size_t len) { binaryAll((const uint8_t *)message, len); } -void AsyncWebSocket::binaryAll(const char *message) -{ +void AsyncWebSocket::binaryAll(const char * message) { binaryAll(message, strlen(message)); } -void AsyncWebSocket::binaryAll(const String &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) - { +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, ...){ +size_t AsyncWebSocket::printf(uint32_t id, const char * format, ...) { AsyncWebSocketClient * c = client(id); - if (c) - { + if (c) { va_list arg; va_start(arg, format); size_t len = c->printf(format, arg); @@ -1087,10 +1060,9 @@ size_t AsyncWebSocket::printf(uint32_t id, const char *format, ...){ return 0; } -size_t AsyncWebSocket::printfAll(const char *format, ...) -{ +size_t AsyncWebSocket::printfAll(const char * format, ...) { va_list arg; - char *temp = new char[MAX_PRINTF_LEN]; + char * temp = new char[MAX_PRINTF_LEN]; if (!temp) return 0; @@ -1102,7 +1074,7 @@ size_t AsyncWebSocket::printfAll(const char *format, ...) std::shared_ptr> buffer = std::make_shared>(len); va_start(arg, format); - vsnprintf( (char *)buffer->data(), len + 1, format, arg); + vsnprintf((char *)buffer->data(), len + 1, format, arg); va_end(arg); textAll(buffer); @@ -1110,23 +1082,22 @@ size_t AsyncWebSocket::printfAll(const char *format, ...) } #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; +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, ...) -{ +size_t AsyncWebSocket::printfAll_P(PGM_P formatP, ...) { va_list arg; - char *temp = new char[MAX_PRINTF_LEN]; + char * temp = new char[MAX_PRINTF_LEN]; if (!temp) return 0; @@ -1145,15 +1116,15 @@ size_t AsyncWebSocket::printfAll_P(PGM_P formatP, ...) 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" }; +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) @@ -1165,11 +1136,11 @@ const char __WS_STR_UUID[] PROGMEM = { "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" }; #define WS_STR_ACCEPT FPSTR(__WS_STR_ACCEPT) #define WS_STR_UUID FPSTR(__WS_STR_UUID) -bool AsyncWebSocket::canHandle(AsyncWebServerRequest *request){ - if(!_enabled) +bool AsyncWebSocket::canHandle(AsyncWebServerRequest * request) { + if (!_enabled) return false; - if(request->method() != HTTP_GET || !request->url().equals(_url) || !request->isExpectedRequestedConnType(RCT_WS)) + if (request->method() != HTTP_GET || !request->url().equals(_url) || !request->isExpectedRequestedConnType(RCT_WS)) return false; request->addInterestingHeader(WS_STR_CONNECTION); @@ -1182,47 +1153,40 @@ bool AsyncWebSocket::canHandle(AsyncWebServerRequest *request){ return true; } -void AsyncWebSocket::handleRequest(AsyncWebServerRequest *request) -{ - if (!request->hasHeader(WS_STR_VERSION) || !request->hasHeader(WS_STR_KEY)) - { +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())) - { + if ((_username.length() && _password.length()) && !request->authenticate(_username.c_str(), _password.c_str())) { return request->requestAuthentication(); } - if (_handshakeHandler != nullptr){ - if(!_handshakeHandler(request)){ + 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); + 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); + 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 * AsyncWebSocket::makeBuffer(size_t size) { AsyncWebSocketMessageBuffer * buffer = new AsyncWebSocketMessageBuffer(size); - if (buffer->length() != size) - { + if (buffer->length() != size) { delete buffer; return nullptr; } else { @@ -1230,11 +1194,9 @@ AsyncWebSocketMessageBuffer * AsyncWebSocket::makeBuffer(size_t size) } } -AsyncWebSocketMessageBuffer * AsyncWebSocket::makeBuffer(uint8_t * data, size_t size) -{ +AsyncWebSocketMessageBuffer * AsyncWebSocket::makeBuffer(uint8_t * data, size_t size) { AsyncWebSocketMessageBuffer * buffer = new AsyncWebSocketMessageBuffer(data, size); - if (buffer->length() != size) - { + if (buffer->length() != size) { delete buffer; return nullptr; } else { @@ -1247,21 +1209,18 @@ AsyncWebSocketMessageBuffer * AsyncWebSocket::makeBuffer(uint8_t * data, size_t * 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; +AsyncWebSocketResponse::AsyncWebSocketResponse(const String & key, AsyncWebSocket * server) { + _server = server; + _code = 101; _sendContentLength = false; - uint8_t * hash = (uint8_t*)malloc(20); - if(hash == NULL) - { + uint8_t * hash = (uint8_t *)malloc(20); + if (hash == NULL) { _state = RESPONSE_FAILED; return; } - char * buffer = (char *) malloc(33); - if(buffer == NULL) - { + char * buffer = (char *)malloc(33); + if (buffer == NULL) { free(hash); _state = RESPONSE_FAILED; return; @@ -1269,29 +1228,26 @@ AsyncWebSocketResponse::AsyncWebSocketResponse(const String& key, AsyncWebSocket #ifdef ESP8266 sha1(key + WS_STR_UUID, hash); #else - (String&)key += WS_STR_UUID; - mbedtls_sha1_context ctx; - mbedtls_sha1_init(&ctx); - mbedtls_sha1_starts(&ctx); - mbedtls_sha1_update(&ctx, (const unsigned char*)key.c_str(), key.length()); - mbedtls_sha1_finish(&ctx, hash); - mbedtls_sha1_free(&ctx); + 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); + 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); + addHeader(WS_STR_ACCEPT, buffer); free(buffer); free(hash); } -void AsyncWebSocketResponse::_respond(AsyncWebServerRequest *request) -{ - if(_state == RESPONSE_FAILED) - { +void AsyncWebSocketResponse::_respond(AsyncWebServerRequest * request) { + if (_state == RESPONSE_FAILED) { request->client()->close(true); return; } @@ -1300,12 +1256,11 @@ void AsyncWebSocketResponse::_respond(AsyncWebServerRequest *request) _state = RESPONSE_WAIT_ACK; } -size_t AsyncWebSocketResponse::_ack(AsyncWebServerRequest *request, size_t len, uint32_t time) -{ +size_t AsyncWebSocketResponse::_ack(AsyncWebServerRequest * request, size_t len, uint32_t time) { (void)time; - if(len) + 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 index ac49d2b82..b744a08bd 100644 --- a/lib/ESPAsyncWebServer/src/AsyncWebSocket.h +++ b/lib/ESPAsyncWebServer/src/AsyncWebSocket.h @@ -89,8 +89,8 @@ 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; + friend AsyncWebSocket; + friend AsyncWebSocketClient; private: std::shared_ptr> _buffer; @@ -98,114 +98,165 @@ class AsyncWebSocketMessageBuffer { public: AsyncWebSocketMessageBuffer(); AsyncWebSocketMessageBuffer(size_t size); - AsyncWebSocketMessageBuffer(uint8_t* data, 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(); } + bool reserve(size_t size); + uint8_t * get() { + return _buffer->data(); + } + size_t length() const { + return _buffer->size(); + } }; -class AsyncWebSocketMessage -{ -private: +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{}; + 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); + 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; } + 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); + 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; + AsyncClient * _client; + AsyncWebSocket * _server; + uint32_t _clientId; + AwsClientStatus _status; AsyncWebLock _lock; std::deque _controlQueue; std::deque _messageQueue; + bool closeWhenFull = true; - uint8_t _pstate; + 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 _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; + void * _tempObject; - AsyncWebSocketClient(AsyncWebServerRequest *request, AsyncWebSocket *server); + 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; } + 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; } + 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); + 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; + void keepAlivePeriod(uint16_t seconds) { + _keepAlivePeriod = seconds * 1000; } - uint16_t keepAlivePeriod(){ - return (uint16_t)(_keepAlivePeriod / 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; + 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))); + 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))); + 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 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 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); + void binary(const String & message); + void binary(const __FlashStringHelper * message, size_t len); + void binary(AsyncWebSocketMessageBuffer * buffer); bool canSend() const; @@ -215,114 +266,133 @@ class AsyncWebSocketClient { void _onPoll(); void _onTimeout(uint32_t time); void _onDisconnect(); - void _onData(void *pbuf, size_t plen); + void _onData(void * pbuf, size_t plen); }; -typedef std::function AwsHandshakeHandler; -typedef std::function AwsEventHandler; +typedef std::function AwsHandshakeHandler; +typedef std::function AwsEventHandler; //WebServer Handler implementation that plays the role of a socket server -class AsyncWebSocket: public AsyncWebHandler { +class AsyncWebSocket : public AsyncWebHandler { private: - String _url; + String _url; std::list _clients; - uint32_t _cNextId; - AwsEventHandler _eventHandler; - AwsHandshakeHandler _handshakeHandler; - bool _enabled; - AsyncWebLock _lock; + uint32_t _cNextId; + AwsEventHandler _eventHandler; + AwsHandshakeHandler _handshakeHandler; + bool _enabled; + AsyncWebLock _lock; public: - AsyncWebSocket(const String& url); + AsyncWebSocket(const String & url); ~AsyncWebSocket(); - const char * url() const { return _url.c_str(); } - void enable(bool e){ _enabled = e; } - bool enabled() const { return _enabled; } + 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; + size_t count() const; AsyncWebSocketClient * client(uint32_t id); - bool hasClient(uint32_t id){ return client(id) != NULL; } + 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 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 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, 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(std::shared_ptr> buffer); - void textAll(const uint8_t *message, size_t len); + 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); // need to convert - void textAll(AsyncWebSocketMessageBuffer *buffer); + 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, 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); - 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); - 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))); + 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))); + 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))); + size_t printfAll_P(PGM_P formatP, ...) __attribute__((format(printf, 2, 3))); //event listener - void onEvent(AwsEventHandler handler){ - _eventHandler = handler; + void onEvent(AwsEventHandler handler) { + _eventHandler = handler; } // Handshake Handler - void handleHandshake(AwsHandshakeHandler handler){ - _handshakeHandler = 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; + 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); + // messagebuffer functions/objects. + AsyncWebSocketMessageBuffer * makeBuffer(size_t size = 0); + AsyncWebSocketMessageBuffer * makeBuffer(uint8_t * data, size_t size); - const std::list &getClients() const { return _clients; } + 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 { +class AsyncWebSocketResponse : public AsyncWebServerResponse { private: - String _content; - AsyncWebSocket *_server; + 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; } + 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_ */ +#endif /* ASYNCWEBSOCKET_H_ */ \ No newline at end of file diff --git a/lib/ESPAsyncWebServer/src/ESPAsyncWebServer.h b/lib/ESPAsyncWebServer/src/ESPAsyncWebServer.h index 8d211aef1..7526b6254 100644 --- a/lib/ESPAsyncWebServer/src/ESPAsyncWebServer.h +++ b/lib/ESPAsyncWebServer/src/ESPAsyncWebServer.h @@ -28,7 +28,7 @@ #include #include "FS.h" -#include // added by proddy +#include // added by proddy for EMS-ESP #include "StringArray.h" @@ -330,7 +330,7 @@ class AsyncWebServerRequest { 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); - // added by proddy + // added by proddy for EMS-ESP AsyncWebServerResponse * beginResponse(const String & contentType, const uint8_t * content, size_t len); size_t headers() const; // get header count @@ -532,7 +532,7 @@ typedef std::function ArRequestHandlerFun typedef std::function ArUploadHandlerFunction; typedef std::function ArBodyHandlerFunction; -typedef std::function ArJsonRequestHandlerFunction; // added by proddy +typedef std::function ArJsonRequestHandlerFunction; // added by proddy for EMS-ESP class AsyncWebServer { protected: @@ -566,7 +566,7 @@ class AsyncWebServer { AsyncCallbackWebHandler & on(const char * uri, WebRequestMethodComposite method, ArRequestHandlerFunction onRequest, ArUploadHandlerFunction onUpload, ArBodyHandlerFunction onBody); - void on(const char * uri, ArJsonRequestHandlerFunction onRequest); // added by proddy + 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); diff --git a/lib/ESPAsyncWebServer/src/WebAuthentication.cpp b/lib/ESPAsyncWebServer/src/WebAuthentication.cpp index a1e6459e2..656adbc61 100644 --- a/lib/ESPAsyncWebServer/src/WebAuthentication.cpp +++ b/lib/ESPAsyncWebServer/src/WebAuthentication.cpp @@ -21,7 +21,7 @@ #include "WebAuthentication.h" #include #ifdef ESP32 -#include "mbedtls/md5.h" +#include #else #include "md5.h" #endif @@ -29,220 +29,221 @@ // 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; +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) + 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) + if (strlen(hash) != encodedLen) #else - if (strlen(hash) != encodedLen - 1) + if (strlen(hash) != encodedLen - 1) #endif - return false; + 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){ + 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 true; - } - delete[] toencode; - delete[] encoded; - return false; + return false; } -static bool getMD5(uint8_t * data, uint16_t len, char * output){//33 bytes or more +static bool getMD5(uint8_t * data, uint16_t len, char * output) { //33 bytes or more #ifdef ESP32 - mbedtls_md5_context _ctx; + MD5Builder md5; + md5.begin(); + md5.add(data, len); + md5.calculate(); + md5.getChars(output); #else md5_context_t _ctx; -#endif - uint8_t i; - uint8_t * _buf = (uint8_t*)malloc(16); - if(_buf == NULL) - return false; - memset(_buf, 0x00, 16); -#ifdef ESP32 -#if ESP_IDF_VERSION_MAJOR < 5 - mbedtls_md5_init(&_ctx); - mbedtls_md5_starts_ret(&_ctx); - mbedtls_md5_update_ret(&_ctx, data, len); - mbedtls_md5_finish_ret(&_ctx, _buf); -#else - mbedtls_md5_init(&_ctx); - mbedtls_md5_starts(&_ctx); - mbedtls_md5_update(&_ctx, data, len); - mbedtls_md5_finish(&_ctx, _buf); -#endif -#else - MD5Init(&_ctx); - MD5Update(&_ctx, data, len); - MD5Final(_buf, &_ctx); -#endif - for(i = 0; i < 16; i++) { - sprintf_P(output + (i * 2), PSTR("%02x"), _buf[i]); - } - free(_buf); - 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; -} + uint8_t * _buf = (uint8_t *)malloc(16); + if (_buf == NULL) + return false; + memset(_buf, 0x00, 16); -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; -} + MD5Init(&_ctx); + MD5Update(&_ctx, data, len); + MD5Final(_buf, &_ctx); -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); + for (uint8_t i = 0; i < 16; i++) { + sprintf_P(output + (i * 2), PSTR("%02x"), _buf[i]); } - 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"); + free(_buf); +#endif return true; - } - - //os_printf("AUTH FAIL: password\n"); - return false; } + +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/WebServer.cpp b/lib/ESPAsyncWebServer/src/WebServer.cpp index 47545b4e2..5f7309b7e 100644 --- a/lib/ESPAsyncWebServer/src/WebServer.cpp +++ b/lib/ESPAsyncWebServer/src/WebServer.cpp @@ -175,7 +175,7 @@ AsyncCallbackWebHandler & AsyncWebServer::on(const char * uri, ArRequestHandlerF return *handler; } -// added by proddy +// added by proddy for EMS-ESP void AsyncWebServer::on(const char * uri, ArJsonRequestHandlerFunction onRequest) { auto * handler = new AsyncCallbackJsonWebHandler(uri, onRequest); addHandler(handler); diff --git a/lib/ESPAsyncWebServer/src/port/SHA1Builder.cpp b/lib/ESPAsyncWebServer/src/port/SHA1Builder.cpp new file mode 100644 index 000000000..b41754dd0 --- /dev/null +++ b/lib/ESPAsyncWebServer/src/port/SHA1Builder.cpp @@ -0,0 +1,284 @@ +/* + * 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 new file mode 100644 index 000000000..5049cde39 --- /dev/null +++ b/lib/ESPAsyncWebServer/src/port/SHA1Builder.h @@ -0,0 +1,39 @@ +// 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/espMqttClient/README.md b/lib/espMqttClient/README.md index 586d3339e..c897d3d35 100644 --- a/lib/espMqttClient/README.md +++ b/lib/espMqttClient/README.md @@ -3,50 +3,16 @@ MQTT client library for the Espressif devices ESP8266 and ESP32 on the Arduino framework. Aims to be a non-blocking, fully compliant MQTT 3.1.1 client. -![platformio](https://github.com/bertmelis/espMqttClient/actions/workflows/build_platformio.yml/badge.svg) -![cpplint](https://github.com/bertmelis/espMqttClient/actions/workflows/cpplint.yml/badge.svg) -![cppcheck](https://github.com/bertmelis/espMqttClient/actions/workflows/cppcheck.yml/badge.svg) -[![PlatformIO Registry](https://badges.registry.platformio.org/packages/bertmelis/library/espMqttClient.svg)](https://registry.platformio.org/libraries/bertmelis/espMqttClient) +Copy of -# Features +Based on Version 1.7.0 - -- MQTT 3.1.1 compliant library -- Sending and receiving at all QoS levels -- TCP and TCP/TLS using standard WiFiClient and WiFiClientSecure connections -- Virtually unlimited incoming and outgoing payload sizes -- Readable and understandable code -- Fully async clients available via [AsyncTCP](https://github.com/me-no-dev/AsyncTCP) or [ESPAsnycTCP](https://github.com/me-no-dev/ESPAsyncTCP) (no TLS supported) -- Supported platforms: - - Espressif ESP8266 and ESP32 using the Arduino framework -- Basic Linux compatibility*. This includes WSL on Windows +with additional changes to support EMS-ESP such as compiling with Tasmota and not using `SecureWifiClient` in these two files: - > Linux compatibility is mainly for automatic testing. It relies on a quick and dirty Arduino-style `Client` with a POSIX TCP client underneath and Arduino-style `IPAddress` class. These are lacking many features needed for proper Linux support. - -# Documentation - -See [documentation](https://www.emelis.net/espMqttClient/) and the [examples](examples/). - -## Limitations - -### MQTT 3.1.1 Compliancy - -Outgoing messages and session data are not stored in non-volatile memory. Any events like loss of power or sudden resets result in loss of data. Despite this limitation, one could still consider this library as fully complaint based on the non normative remark in point 4.1.1 of the specification. - -### Non-blocking - -This library aims to be fully non-blocking. It is however limited by the underlying `WiFiClient` library which is part of the Arduino framework and has a blocking `connect` method. This is not an issue on ESP32 because the call is offloaded to a separate task. On ESP8266 however, connecting will block until succesful or until the connection timeouts. - -If you need a fully asynchronous MQTT client, you can use `espMqttClientAsync` which uses AsyncTCP/ESPAsyncTCP under the hood. These underlying libraries do not support TLS (anymore). I will not provide support TLS for the async client. - -# Bugs and feature requests - -Please use Github's facilities to get in touch. - -# About this library - -This client wouldn't exist without [Async-mqtt-client](https://github.com/marvinroger/async-mqtt-client). It has been my go-to MQTT client for many years. It was fast, reliable and had features that were non-existing in alternative libraries. However, the underlying async TCP libraries are lacking updates, especially updates related to secure connections. Adapting this library to use up-to-date TCP clients would not be trivial. I eventually decided to write my own MQTT library, from scratch. - -The result is an almost non-blocking library with no external dependencies. The library is almost a drop-in replacement for the async-mqtt-client except a few parameter type changes (eg. `uint8_t*` instead of `char*` for payloads). +``` + src/espMqttClient.cpp + src/Transport/ClientSecureSync.h +``` # License diff --git a/lib/espMqttClient/src/Config.h b/lib/espMqttClient/src/Config.h index d6d1a0e63..935f7e1f5 100644 --- a/lib/espMqttClient/src/Config.h +++ b/lib/espMqttClient/src/Config.h @@ -8,12 +8,8 @@ the LICENSE file. #pragma once -#ifndef TASMOTA_SDK -#define EMC_CLIENT_SECURE -#endif - #ifndef EMC_TX_TIMEOUT -#define EMC_TX_TIMEOUT 2000 +#define EMC_TX_TIMEOUT 10000 #endif #ifndef EMC_RX_BUFFER_SIZE @@ -64,3 +60,16 @@ the LICENSE file. #ifndef EMC_USE_WATCHDOG #define EMC_USE_WATCHDOG 0 #endif + +#ifndef EMC_USE_MEMPOOL +#define EMC_USE_MEMPOOL 0 +#endif + +#if EMC_USE_MEMPOOL + #ifndef EMC_NUM_POOL_ELEMENTS + #define EMC_NUM_POOL_ELEMENTS 32 + #endif + #ifndef EMC_SIZE_POOL_ELEMENTS + #define EMC_SIZE_POOL_ELEMENTS 128 + #endif +#endif diff --git a/lib/espMqttClient/src/MemoryPool/LICENSE b/lib/espMqttClient/src/MemoryPool/LICENSE new file mode 100644 index 000000000..526a0c7cf --- /dev/null +++ b/lib/espMqttClient/src/MemoryPool/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 Bert Melis + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/lib/espMqttClient/src/MemoryPool/README.md b/lib/espMqttClient/src/MemoryPool/README.md new file mode 100644 index 000000000..81b6fd43a --- /dev/null +++ b/lib/espMqttClient/src/MemoryPool/README.md @@ -0,0 +1,105 @@ +# Memory Pool + +EARLY VERSION. USE AT OWN RISK. + +### Description + +This is a simple memory pool that doesn't solve the fragmentation problem but contains it. Inside the pool you will still suffer memory fragmentation. The upside is that you're not restricted on memory size. As long as it fits in the pool, you can request any size! + +For applications where the (maximum) size to allocate is known, a simple fixed block size memory pool is available. There is no memory fragmentation happening in this case. The downside is wastage of memory if you need less then the specified blocksize. + +#### Features + +- pool memory is statically allocated +- pool size adjusts on architecture +- no size calculation required: input number of blocks and size of block +- header-only library +- Variable size pool: no restriction on allocated size +- Variable size pool: malloc and free are O(n); The number of allocated blocks affects lookup. +- Fixed size pool: malloc and free are O(1). + +[![Test with Platformio](https://github.com/bertmelis/MemoryPool/actions/workflows/test-platformio.yml/badge.svg)](https://github.com/bertmelis/MemoryPool/actions/workflows/test-platformio.yml) +[![cpplint](https://github.com/bertmelis/MemoryPool/actions/workflows/cpplint.yml/badge.svg)](https://github.com/bertmelis/MemoryPool/actions/workflows/cpplint.yml) + + +### Usage + +#### Variable size pool + +```cpp +#include + +Struct MyStruct { + unsigned int id; + std::size_t size; + unsigned char data[256]; +}; + +// pool will be able to hold 10 blocks the size of MyStruct +MemoryPool::Variable<10, sizeof(MyStruct)> pool; + +// you can allocate the specified blocksize +// allocation is done in number of 'unsigned char' +MyStruct* s = reinterpret_cast(pool.malloc(sizeof(MyStruct))); + +// you can allocate less than the specified blocksize +int* i = reinterpret_cast(pool.malloc(sizeof(int))); + +// you can allocate more than the specified blocksize +unsigned char* m = reinterpret_cast(pool.malloc(400)); + +pool.free(s); +pool.free(i); +pool.free(m); +``` + +#### Fixed size pool + +```cpp +#include + +Struct MyStruct { + unsigned int id; + std::size_t size; + unsigned char data[256]; +}; + +// pool will be able to hold 10 blocks the size of MyStruct +MemoryPool::Fixed<10, sizeof(MyStruct)> pool; + +// there is no size argument in the malloc function! +MyStruct* s = reinterpret_cast(pool.malloc()); + +// you can allocate less than the specified blocksize +int* i = reinterpret_cast(pool.malloc()); + +pool.free(s); +pool.free(i); +``` + +#### How it works + +##### Variable size pool + +Free blocks are organized as a linked list with their header (contains pointer to next and size). An allocated block also has this header with it's pointer set to `nullptr`. Therefore, each allocation wastes memory the size of the header (`sizeof(void*) + sizeof(std::size_t)`). On creation, the pool calculations the needed space to store the number of blocks wich each their header. + +However, memory allocation isn't restricted the the specified blocksize. So in reality, you can allocate more if you allocate larger chunks because less memory blocks means less headers. After all, memory needs to be contiguous. + +If you inspect the pool you'll see that a free pool only has one big block. + +Allocation is linear: the pool is iterated until a suitable spot is found. +Freeing is also linear as the pool is traversed to insert the chunk in the linked list of free blocks + +When freeing, free blocks which are adjacent are combined into one. + +##### Fixed size pool + +The fixed size pool is implemented as an array. Free blocks are saved as a linked list in this array. + +### Bugs and feature requests + +Please use Github's facilities to get in touch. + +### License + +This library is released under the MIT Licence. A copy is included in the repo. diff --git a/lib/espMqttClient/src/MemoryPool/keywords.txt b/lib/espMqttClient/src/MemoryPool/keywords.txt new file mode 100644 index 000000000..ef87ce227 --- /dev/null +++ b/lib/espMqttClient/src/MemoryPool/keywords.txt @@ -0,0 +1,16 @@ +# Datatypes (KEYWORD1) +Fixed KEYWORD1 +Variable KEYWORD1 + +# Methods and Functions (KEYWORD2) +malloc KEYWORD2 +free KEYWORD2 +freeMemory KEYWORD2 +maxBlockSize KEYWORD2 +print KEYWORD2 + +# Structures (KEYWORD3) +# structure KEYWORD3 + +# Constants (LITERAL1) +MemoryPool LITERAL1 diff --git a/lib/espMqttClient/src/MemoryPool/library.json b/lib/espMqttClient/src/MemoryPool/library.json new file mode 100644 index 000000000..f9e61165d --- /dev/null +++ b/lib/espMqttClient/src/MemoryPool/library.json @@ -0,0 +1,21 @@ +{ + "name": "MemoryPool", + "keywords": "memory", + "description": "A simple memory pool for fixed and variable sizes", + "authors": + { + "name": "Bert Melis", + "url": "https://github.com/bertmelis" + }, + "license": "MIT", + "homepage": "https://github.com/bertmelis/MemoryPool", + "repository": + { + "type": "git", + "url": "https://github.com/bertmelis/MemoryPool.git" + }, + "version": "0.1.0", + "frameworks": "*", + "platforms": "*", + "headers": ["MemoryPool.h"] + } \ No newline at end of file diff --git a/lib/espMqttClient/src/MemoryPool/library.properties b/lib/espMqttClient/src/MemoryPool/library.properties new file mode 100644 index 000000000..a46b50f9a --- /dev/null +++ b/lib/espMqttClient/src/MemoryPool/library.properties @@ -0,0 +1,10 @@ +name=MemoryPool +version=0.1.0 +author=Bert Melis +maintainer=Bert Melis +sentence=A simple memory pool for fixed and variable sizes +paragraph= +category=Other +url=https://github.com/bertmelis/MemoryPool +architectures=* +includes=MemoryPool.h \ No newline at end of file diff --git a/lib/espMqttClient/src/MemoryPool/src/Fixed.h b/lib/espMqttClient/src/MemoryPool/src/Fixed.h new file mode 100644 index 000000000..b68dbd136 --- /dev/null +++ b/lib/espMqttClient/src/MemoryPool/src/Fixed.h @@ -0,0 +1,119 @@ +/* +Copyright (c) 2024 Bert Melis. All rights reserved. + +This work is licensed under the terms of the MIT license. +For a copy, see or +the LICENSE file. +*/ + +#pragma once + +#include // std::size_t +#include // assert +#if _GLIBCXX_HAS_GTHREADS +#include // NOLINT [build/c++11] std::mutex, std::lock_guard +#else +#warning "The memory pool is not thread safe" +#endif + +#ifdef MEMPOL_DEBUG +#include +#endif + +namespace MemoryPool { + +template +class Fixed { + public: + Fixed() // cppcheck-suppress uninitMemberVar + : _buffer{0} + , _head(_buffer) { + unsigned char* b = _head; + std::size_t adjustedBlocksize = sizeof(std::size_t) > blocksize ? sizeof(std::size_t) : blocksize; + for (std::size_t i = 0; i < nrBlocks - 1; ++i) { + *reinterpret_cast(b) = b + adjustedBlocksize; + b += adjustedBlocksize; + } + *reinterpret_cast(b) = nullptr; + } + + // no copy nor move + Fixed (const Fixed&) = delete; + Fixed& operator= (const Fixed&) = delete; + + void* malloc() { + #if _GLIBCXX_HAS_GTHREADS + const std::lock_guard lockGuard(_mutex); + #endif + if (_head) { + void* retVal = _head; + _head = *reinterpret_cast(_head); + return retVal; + } + return nullptr; + } + + void free(void* ptr) { + if (!ptr) return; + #if _GLIBCXX_HAS_GTHREADS + const std::lock_guard lockGuard(_mutex); + #endif + *reinterpret_cast(ptr) = _head; + _head = reinterpret_cast(ptr); + } + + std::size_t freeMemory() { + #if _GLIBCXX_HAS_GTHREADS + const std::lock_guard lockGuard(_mutex); + #endif + unsigned char* i = _head; + std::size_t retVal = 0; + while (i) { + retVal += blocksize; + i = reinterpret_cast(i)[0]; + } + return retVal; + } + + #ifdef MEMPOL_DEBUG + void print() { + std::size_t adjustedBlocksize = sizeof(std::size_t) > blocksize ? sizeof(std::size_t) : blocksize; + std::cout << "+--------------------" << std::endl; + std::cout << "|start:" << reinterpret_cast(_buffer) << std::endl; + std::cout << "|blocks:" << nrBlocks << std::endl; + std::cout << "|blocksize:" << adjustedBlocksize << std::endl; + std::cout << "|head: " << reinterpret_cast(_head) << std::endl; + unsigned char* currentBlock = _buffer; + + for (std::size_t i = 0; i < nrBlocks; ++i) { + std::cout << "|" << i + 1 << ": " << reinterpret_cast(currentBlock) << std::endl; + if (_isFree(currentBlock)) { + std::cout << "| free" << std::endl; + std::cout << "| next: " << reinterpret_cast(*reinterpret_cast(currentBlock)) << std::endl; + } else { + std::cout << "| allocated" << std::endl; + } + currentBlock += adjustedBlocksize; + } + std::cout << "+--------------------" << std::endl; + } + + bool _isFree(const unsigned char* ptr) { + unsigned char* b = _head; + while (b) { + if (b == ptr) return true; + b = *reinterpret_cast(b); + } + return false; + } + #endif + + private: + unsigned char _buffer[nrBlocks * (sizeof(std::size_t) > blocksize ? sizeof(std::size_t) : blocksize)]; + unsigned char* _head; + #if _GLIBCXX_HAS_GTHREADS + std::mutex _mutex; + #endif +}; + +} // end namespace MemoryPool diff --git a/lib/espMqttClient/src/MemoryPool/src/MemoryPool.h b/lib/espMqttClient/src/MemoryPool/src/MemoryPool.h new file mode 100644 index 000000000..5b198eaf2 --- /dev/null +++ b/lib/espMqttClient/src/MemoryPool/src/MemoryPool.h @@ -0,0 +1,12 @@ +/* +Copyright (c) 2024 Bert Melis. All rights reserved. + +This work is licensed under the terms of the MIT license. +For a copy, see or +the LICENSE file. +*/ + +#pragma once + +#include "Variable.h" +#include "Fixed.h" diff --git a/lib/espMqttClient/src/MemoryPool/src/Variable.h b/lib/espMqttClient/src/MemoryPool/src/Variable.h new file mode 100644 index 000000000..563bf4978 --- /dev/null +++ b/lib/espMqttClient/src/MemoryPool/src/Variable.h @@ -0,0 +1,242 @@ +/* +Copyright (c) 2024 Bert Melis. All rights reserved. + +This work is licensed under the terms of the MIT license. +For a copy, see or +the LICENSE file. +*/ + +#pragma once + +#include // std::size_t +#include // assert +#if _GLIBCXX_HAS_GTHREADS +#include // NOLINT [build/c++11] std::mutex, std::lock_guard +#else +#warning "The memory pool is not thread safe" +#endif + +#ifdef MEMPOL_DEBUG +#include +#endif + +namespace MemoryPool { + +template +class Variable { + public: + Variable() + : _buffer{0} + , _head(nullptr) + #ifdef MEMPOL_DEBUG + , _bufferSize(0) + #endif + { + std::size_t _normBlocksize = blocksize / sizeof(BlockHeader) + ((blocksize % sizeof(BlockHeader)) ? 1 : 0); + size_t nrBlocksToAlloc = nrBlocks * (_normBlocksize + 1); + BlockHeader* h = reinterpret_cast(_buffer); + h->next = nullptr; + h->size = nrBlocksToAlloc; + _head = h; + + #ifdef MEMPOL_DEBUG + _bufferSize = nrBlocksToAlloc; + #endif + } + + // no copy nor move + Variable (const Variable&) = delete; + Variable& operator= (const Variable&) = delete; + + void* malloc(size_t size) { + #if _GLIBCXX_HAS_GTHREADS + const std::lock_guard lockGuard(_mutex); + #endif + if (size == 0) return nullptr; + + size = (size / sizeof(BlockHeader) + (size % sizeof(BlockHeader) != 0)) + 1; // count by BlockHeader size, add 1 for header + + #ifdef MEMPOL_DEBUG + std::cout << "malloc (raw) " << size << std::endl; + std::cout << "malloc (adj) " << size << " - "; + #endif + + BlockHeader* currentBlock = _head; + BlockHeader* previousBlock = nullptr; + void* retVal = nullptr; + + // iterate through linked free blocks + while (currentBlock) { + // consume whole block is size equals required size + if (currentBlock->size == size) { + if (previousBlock) previousBlock->next = currentBlock->next; + break; + + // split block if size is larger and add second part to list of free blocks + } else if (currentBlock->size > size) { + BlockHeader* newBlock = currentBlock + size; + if (previousBlock) previousBlock->next = newBlock; + newBlock->next = currentBlock->next; + newBlock->size = currentBlock->size - size; + currentBlock->next = newBlock; + break; + } + previousBlock = currentBlock; + currentBlock = currentBlock->next; + } + + if (currentBlock) { + if (currentBlock == _head) { + _head = currentBlock->next; + } + currentBlock->size = size; + currentBlock->next = nullptr; // used when freeing memory + retVal = currentBlock + 1; + #ifdef MEMPOL_DEBUG + std::cout << "ok" << std::endl; + #endif + } else { + #ifdef MEMPOL_DEBUG + std::cout << "nok" << std::endl; + #endif + (void)0; + } + + return retVal; + } + + void free(void* ptr) { + if (!ptr) return; + // check if ptr points to region in _buffer + + #ifdef MEMPOL_DEBUG + std::cout << "free " << static_cast(reinterpret_cast(ptr) - 1) << std::endl; + #endif + + #if _GLIBCXX_HAS_GTHREADS + const std::lock_guard lockGuard(_mutex); + #endif + + BlockHeader* toFree = reinterpret_cast(ptr) - 1; + BlockHeader* previous = reinterpret_cast(_buffer); + BlockHeader* next = _head; + + // toFree is the only free block + if (!next) { + _head = toFree; + return; + } + + while (previous) { + if (!next || toFree < next) { + // 1. add block to linked list of free blocks + if (toFree < _head) { + toFree->next = _head; + _head = toFree; + } else { + previous->next = toFree; + toFree->next = next; + } + + // 2. merge with previous if adjacent + if (toFree > _head && toFree == previous + previous->size) { + previous->size += toFree->size; + previous->next = toFree->next; + toFree = previous; // used in next check + } + + // 3. merge with next if adjacent + if (toFree + toFree->size == next) { + toFree->size += next->size; + toFree->next = next->next; + } + + // 4. done + return; + } + previous = next; + next = next->next; + } + } + + std::size_t freeMemory() { + #if _GLIBCXX_HAS_GTHREADS + const std::lock_guard lockGuard(_mutex); + #endif + size_t retVal = 0; + BlockHeader* currentBlock = reinterpret_cast(_head); + + while (currentBlock) { + retVal += currentBlock->size - 1; + currentBlock = currentBlock->next; + } + + return retVal * sizeof(BlockHeader); + } + + std::size_t maxBlockSize() { + #if _GLIBCXX_HAS_GTHREADS + const std::lock_guard lockGuard(_mutex); + #endif + size_t retVal = 0; + BlockHeader* currentBlock = reinterpret_cast(_head); + + while (currentBlock) { + retVal = (currentBlock->size - 1 > retVal) ? currentBlock->size - 1 : retVal; + currentBlock = currentBlock->next; + } + + return retVal * sizeof(BlockHeader); + } + + #ifdef MEMPOL_DEBUG + void print() { + std::cout << "+--------------------" << std::endl; + std::cout << "|start:" << static_cast(_buffer) << std::endl; + std::cout << "|size:" << _bufferSize << std::endl; + std::cout << "|headersize:" << sizeof(BlockHeader) << std::endl; + std::cout << "|head: " << static_cast(_head) << std::endl; + BlockHeader* nextFreeBlock = _head; + BlockHeader* currentBlock = reinterpret_cast(_buffer); + size_t blockNumber = 1; + while (currentBlock < reinterpret_cast(_buffer) + _bufferSize) { + std::cout << "|" << blockNumber << ": " << static_cast(currentBlock) << std::endl; + std::cout << "| " << static_cast(currentBlock->next) << std::endl; + std::cout << "| " << currentBlock->size << std::endl; + if (currentBlock == nextFreeBlock) { + std::cout << "| free" << std::endl; + nextFreeBlock = nextFreeBlock->next; + } else { + std::cout << "| allocated" << std::endl; + } + ++blockNumber; + currentBlock += currentBlock->size; + } + std::cout << "+--------------------" << std::endl; + } + #endif + + private: + struct BlockHeader { + BlockHeader* next; + std::size_t size; + }; + /* + pool size is aligned to sizeof(BlockHeader). + requested blocksize is therefore multiple of blockheader (rounded up) + total size = nr requested blocks * multiplier * blockheadersize + + see constructor for calculation + */ + unsigned char _buffer[(nrBlocks * ((blocksize / sizeof(BlockHeader) + ((blocksize % sizeof(BlockHeader)) ? 1 : 0)) + 1)) * sizeof(BlockHeader)]; + BlockHeader* _head; + #if _GLIBCXX_HAS_GTHREADS + std::mutex _mutex; + #endif + + #ifdef MEMPOL_DEBUG + std::size_t _bufferSize; + #endif +}; + +} // end namespace MemoryPool diff --git a/lib/espMqttClient/src/MqttClient.cpp b/lib/espMqttClient/src/MqttClient.cpp index 8b780cf3f..dc21f7456 100644 --- a/lib/espMqttClient/src/MqttClient.cpp +++ b/lib/espMqttClient/src/MqttClient.cpp @@ -14,721 +14,733 @@ using espMqttClientTypes::DisconnectReason; using espMqttClientTypes::Error; MqttClient::MqttClient(espMqttClientTypes::UseInternalTask useInternalTask, uint8_t priority, uint8_t core) - : _useInternalTask(useInternalTask) - , _transport(nullptr) - , _onConnectCallback(nullptr) - , _onDisconnectCallback(nullptr) - , _onSubscribeCallback(nullptr) - , _onUnsubscribeCallback(nullptr) - , _onMessageCallback(nullptr) - , _onPublishCallback(nullptr) - , _onErrorCallback(nullptr) - , _clientId(nullptr) - , _ip() - , _host(nullptr) - , _port(1883) - , _useIp(false) - , _keepAlive(15000) - , _cleanSession(true) - , _username(nullptr) - , _password(nullptr) - , _willTopic(nullptr) - , _willPayload(nullptr) - , _willPayloadLength(0) - , _willQos(0) - , _willRetain(false) - , _timeout(EMC_TX_TIMEOUT) - , _state(State::disconnected) - , _generatedClientId{0} - , _packetId(0) +: _useInternalTask(useInternalTask) +, _transport(nullptr) +, _onConnectCallback(nullptr) +, _onDisconnectCallback(nullptr) +, _onSubscribeCallback(nullptr) +, _onUnsubscribeCallback(nullptr) +, _onMessageCallback(nullptr) +, _onPublishCallback(nullptr) +, _onErrorCallback(nullptr) +, _clientId(nullptr) +, _ip() +, _host(nullptr) +, _port(1883) +, _useIp(false) +, _keepAlive(15000) +, _cleanSession(true) +, _username(nullptr) +, _password(nullptr) +, _willTopic(nullptr) +, _willPayload(nullptr) +, _willPayloadLength(0) +, _willQos(0) +, _willRetain(false) +, _timeout(EMC_TX_TIMEOUT) +, _state(State::disconnected) +, _generatedClientId{0} +, _packetId(0) #if defined(ARDUINO_ARCH_ESP32) - , _xSemaphore(nullptr) - , _taskHandle(nullptr) +, _xSemaphore(nullptr) +, _taskHandle(nullptr) #endif - , _rxBuffer{0} - , _outbox() - , _bytesSent(0) - , _parser() - , _lastClientActivity(0) - , _lastServerActivity(0) - , _pingSent(false) - , _disconnectReason(DisconnectReason::TCP_DISCONNECTED) +, _rxBuffer{0} +, _outbox() +, _bytesSent(0) +, _parser() +, _lastClientActivity(0) +, _lastServerActivity(0) +, _pingSent(false) +, _disconnectReason(DisconnectReason::TCP_DISCONNECTED) #if defined(ARDUINO_ARCH_ESP32) && ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_INFO - , _highWaterMark(4294967295) +, _highWaterMark(4294967295) #endif -{ - EMC_GENERATE_CLIENTID(_generatedClientId); + { + EMC_GENERATE_CLIENTID(_generatedClientId); #if defined(ARDUINO_ARCH_ESP32) - _xSemaphore = xSemaphoreCreateMutex(); - EMC_SEMAPHORE_GIVE(); // release before first use - if (_useInternalTask == espMqttClientTypes::UseInternalTask::YES) { - xTaskCreatePinnedToCore((TaskFunction_t)_loop, "mqttclient", EMC_TASK_STACK_SIZE, this, priority, &_taskHandle, core); - } + _xSemaphore = xSemaphoreCreateMutex(); + EMC_SEMAPHORE_GIVE(); // release before first use + if (_useInternalTask == espMqttClientTypes::UseInternalTask::YES) { + xTaskCreatePinnedToCore((TaskFunction_t)_loop, "mqttclient", EMC_TASK_STACK_SIZE, this, priority, &_taskHandle, core); + } #else - (void)useInternalTask; - (void)priority; - (void)core; + (void) useInternalTask; + (void) priority; + (void) core; #endif - _clientId = _generatedClientId; + _clientId = _generatedClientId; } MqttClient::~MqttClient() { - disconnect(true); - _clearQueue(2); + disconnect(true); + _clearQueue(2); #if defined(ARDUINO_ARCH_ESP32) - vSemaphoreDelete(_xSemaphore); - if (_useInternalTask == espMqttClientTypes::UseInternalTask::YES) { -#if EMC_USE_WATCHDOG - esp_task_wdt_delete(_taskHandle); // not sure if this is really needed -#endif - vTaskDelete(_taskHandle); - } + vSemaphoreDelete(_xSemaphore); + if (_useInternalTask == espMqttClientTypes::UseInternalTask::YES) { + #if EMC_USE_WATCHDOG + esp_task_wdt_delete(_taskHandle); // not sure if this is really needed + #endif + vTaskDelete(_taskHandle); + } #endif } bool MqttClient::connected() const { - if (_state == State::connected) - return true; - return false; + if (_state == State::connected) return true; + return false; } bool MqttClient::disconnected() const { - if (_state == State::disconnected) - return true; - return false; + if (_state == State::disconnected) return true; + return false; } bool MqttClient::connect() { - bool result = false; - if (_state == State::disconnected) { - EMC_SEMAPHORE_TAKE(); - if (_addPacketFront(_cleanSession, - _username, - _password, - _willTopic, - _willRetain, - _willQos, - _willPayload, - _willPayloadLength, - (uint16_t)(_keepAlive / 1000), // 32b to 16b doesn't overflow because it comes from 16b orignally - _clientId)) { - result = true; - _state = State::connectingTcp1; -#if defined(ARDUINO_ARCH_ESP32) - if (_useInternalTask == espMqttClientTypes::UseInternalTask::YES) { - vTaskResume(_taskHandle); - } -#endif - } else { - EMC_SEMAPHORE_GIVE(); - emc_log_e("Could not create CONNECT packet"); - _onError(0, Error::OUT_OF_MEMORY); - } - EMC_SEMAPHORE_GIVE(); + bool result = false; + if (_state == State::disconnected) { + EMC_SEMAPHORE_TAKE(); + if (_addPacketFront(_cleanSession, + _username, + _password, + _willTopic, + _willRetain, + _willQos, + _willPayload, + _willPayloadLength, + (uint16_t)(_keepAlive / 1000), // 32b to 16b doesn't overflow because it comes from 16b orignally + _clientId)) { + result = true; + _setState(State::connectingTcp1); + #if defined(ARDUINO_ARCH_ESP32) + if (_useInternalTask == espMqttClientTypes::UseInternalTask::YES) { + vTaskResume(_taskHandle); + } + #endif + } else { + emc_log_e("Could not create CONNECT packet"); + EMC_SEMAPHORE_GIVE(); + _onError(0, Error::OUT_OF_MEMORY); + EMC_SEMAPHORE_TAKE(); } - return result; + EMC_SEMAPHORE_GIVE(); + } + return result; } bool MqttClient::disconnect(bool force) { - if (force && _state != State::disconnected && _state != State::disconnectingTcp1 && _state != State::disconnectingTcp2) { - _state = State::disconnectingTcp1; - return true; - } - if (!force && _state == State::connected) { - _state = State::disconnectingMqtt1; - return true; - } - return false; + if (force && _state != State::disconnected && _state != State::disconnectingTcp1 && _state != State::disconnectingTcp2) { + _setState(State::disconnectingTcp1); + return true; + } + if (!force && _state == State::connected) { + _setState(State::disconnectingMqtt1); + return true; + } + return false; } -uint16_t MqttClient::publish(const char * topic, uint8_t qos, bool retain, const uint8_t * payload, size_t length) { -#if !EMC_ALLOW_NOT_CONNECTED_PUBLISH - if (_state != State::connected) { -#else - if (_state > State::connected) { -#endif - return 0; - } - EMC_SEMAPHORE_TAKE(); - uint16_t packetId = (qos > 0) ? _getNextPacketId() : 1; - if (!_addPacket(packetId, topic, payload, length, qos, retain)) { - emc_log_e("Could not create PUBLISH packet"); - _onError(packetId, Error::OUT_OF_MEMORY); - packetId = 0; - } +uint16_t MqttClient::publish(const char* topic, uint8_t qos, bool retain, const uint8_t* payload, size_t length) { + #if !EMC_ALLOW_NOT_CONNECTED_PUBLISH + if (_state != State::connected) { + #else + if (_state > State::connected) { + #endif + return 0; + } + EMC_SEMAPHORE_TAKE(); + uint16_t packetId = (qos > 0) ? _getNextPacketId() : 1; + if (!_addPacket(packetId, topic, payload, length, qos, retain)) { + emc_log_e("Could not create PUBLISH packet"); EMC_SEMAPHORE_GIVE(); - return packetId; -} - -uint16_t MqttClient::publish(const char * topic, uint8_t qos, bool retain, const char * payload) { - size_t len = strlen(payload); - return publish(topic, qos, retain, reinterpret_cast(payload), len); -} - -uint16_t MqttClient::publish(const char * topic, uint8_t qos, bool retain, espMqttClientTypes::PayloadCallback callback, size_t length) { -#if !EMC_ALLOW_NOT_CONNECTED_PUBLISH - if (_state != State::connected) { -#else - if (_state > State::connected) { -#endif - return 0; - } + _onError(packetId, Error::OUT_OF_MEMORY); EMC_SEMAPHORE_TAKE(); - uint16_t packetId = (qos > 0) ? _getNextPacketId() : 1; - if (!_addPacket(packetId, topic, callback, length, qos, retain)) { - emc_log_e("Could not create PUBLISH packet"); - _onError(packetId, Error::OUT_OF_MEMORY); - packetId = 0; - } + packetId = 0; + } + EMC_SEMAPHORE_GIVE(); + return packetId; +} + +uint16_t MqttClient::publish(const char* topic, uint8_t qos, bool retain, const char* payload) { + size_t len = strlen(payload); + return publish(topic, qos, retain, reinterpret_cast(payload), len); +} + +uint16_t MqttClient::publish(const char* topic, uint8_t qos, bool retain, espMqttClientTypes::PayloadCallback callback, size_t length) { + #if !EMC_ALLOW_NOT_CONNECTED_PUBLISH + if (_state != State::connected) { + #else + if (_state > State::connected) { + #endif + return 0; + } + EMC_SEMAPHORE_TAKE(); + uint16_t packetId = (qos > 0) ? _getNextPacketId() : 1; + if (!_addPacket(packetId, topic, callback, length, qos, retain)) { + emc_log_e("Could not create PUBLISH packet"); EMC_SEMAPHORE_GIVE(); - return packetId; + _onError(packetId, Error::OUT_OF_MEMORY); + EMC_SEMAPHORE_TAKE(); + packetId = 0; + } + EMC_SEMAPHORE_GIVE(); + return packetId; } void MqttClient::clearQueue(bool deleteSessionData) { - _clearQueue(deleteSessionData ? 2 : 0); + EMC_SEMAPHORE_TAKE(); + _clearQueue(deleteSessionData ? 2 : 0); + EMC_SEMAPHORE_GIVE(); } -const char * MqttClient::getClientId() const { - return _clientId; +const char* MqttClient::getClientId() const { + return _clientId; } size_t MqttClient::queueSize() { - size_t ret = 0; - EMC_SEMAPHORE_TAKE(); - ret = _outbox.size(); - EMC_SEMAPHORE_GIVE(); - return ret; + size_t ret = 0; + EMC_SEMAPHORE_TAKE(); + ret = _outbox.size(); + EMC_SEMAPHORE_GIVE(); + return ret; } void MqttClient::loop() { - switch ((State)_state) { // modified by proddy for EMS-ESP compiling standalone + switch (_state) { case State::disconnected: -#if defined(ARDUINO_ARCH_ESP32) - if (_useInternalTask == espMqttClientTypes::UseInternalTask::YES) { - vTaskSuspend(_taskHandle); - } -#endif - break; + #if defined(ARDUINO_ARCH_ESP32) + if (_useInternalTask == espMqttClientTypes::UseInternalTask::YES) { + vTaskSuspend(_taskHandle); + } + #endif + break; case State::connectingTcp1: - if (_useIp ? _transport->connect(_ip, _port) : _transport->connect(_host, _port)) { - _state = State::connectingTcp2; - } else { - _state = State::disconnectingTcp1; - _disconnectReason = DisconnectReason::TCP_DISCONNECTED; - break; - } - // Falling through to speed up connecting on blocking transport 'connect' implementations - [[fallthrough]]; + if (_useIp ? _transport->connect(_ip, _port) : _transport->connect(_host, _port)) { + _setState(State::connectingTcp2); + } else { + _setState(State::disconnectingTcp1); + _disconnectReason = DisconnectReason::TCP_DISCONNECTED; + break; + } + // Falling through to speed up connecting on blocking transport 'connect' implementations + [[fallthrough]]; case State::connectingTcp2: - if (_transport->connected()) { - _parser.reset(); - _lastClientActivity = _lastServerActivity = millis(); - _state = State::connectingMqtt; - } - break; + if (_transport->connected()) { + _parser.reset(); + _lastClientActivity = _lastServerActivity = millis(); + _setState(State::connectingMqtt); + } else if (_transport->disconnected()) { // sync: implemented as "not connected"; async: depending on state of pcb in underlying lib + _setState(State::disconnectingTcp1); + _disconnectReason = DisconnectReason::TCP_DISCONNECTED; + } + break; case State::connectingMqtt: -#if EMC_WAIT_FOR_CONNACK - if (_transport->connected()) { - _sendPacket(); - _checkIncoming(); - _checkPing(); - } else { - _state = State::disconnectingTcp1; - _disconnectReason = DisconnectReason::TCP_DISCONNECTED; - } - break; -#else - // receipt of CONNACK packet will set state to CONNECTED - // client however is allowed to send packets before CONNACK is received - // so we fall through to 'connected' - [[fallthrough]]; -#endif - case State::connected: - [[fallthrough]]; - case State::disconnectingMqtt2: - if (_transport->connected()) { - // CONNECT packet is first in the queue - _checkOutbox(); - _checkIncoming(); - _checkPing(); - _checkTimeout(); - } else { - _state = State::disconnectingTcp1; - _disconnectReason = DisconnectReason::TCP_DISCONNECTED; - } - break; - case State::disconnectingMqtt1: + #if EMC_WAIT_FOR_CONNACK + if (_transport->connected()) { EMC_SEMAPHORE_TAKE(); - if (_outbox.empty()) { - if (!_addPacket(PacketType.DISCONNECT)) { - EMC_SEMAPHORE_GIVE(); - emc_log_e("Could not create DISCONNECT packet"); - _onError(0, Error::OUT_OF_MEMORY); - } else { - _state = State::disconnectingMqtt2; - } - } + _sendPacket(); + _checkIncoming(); + _checkPing(); EMC_SEMAPHORE_GIVE(); + } else { + _setState(State::disconnectingTcp1); + _disconnectReason = DisconnectReason::TCP_DISCONNECTED; + } + break; + #else + // receipt of CONNACK packet will set state to CONNECTED + // client however is allowed to send packets before CONNACK is received + // so we fall through to 'connected' + [[fallthrough]]; + #endif + case State::connected: + [[fallthrough]]; + case State::disconnectingMqtt2: + if (_transport->connected()) { + // CONNECT packet is first in the queue + EMC_SEMAPHORE_TAKE(); _checkOutbox(); _checkIncoming(); _checkPing(); _checkTimeout(); - break; - case State::disconnectingTcp1: - _transport->stop(); - _state = State::disconnectingTcp2; - break; // keep break to accomodate async clients - case State::disconnectingTcp2: - if (_transport->disconnected()) { - _clearQueue(0); - _bytesSent = 0; - _state = State::disconnected; - if (_onDisconnectCallback) - _onDisconnectCallback(_disconnectReason); + EMC_SEMAPHORE_GIVE(); + } else { + _setState(State::disconnectingTcp1); + _disconnectReason = DisconnectReason::TCP_DISCONNECTED; + } + break; + case State::disconnectingMqtt1: + EMC_SEMAPHORE_TAKE(); + if (_outbox.empty()) { + if (!_addPacket(PacketType.DISCONNECT)) { + EMC_SEMAPHORE_GIVE(); + emc_log_e("Could not create DISCONNECT packet"); + _onError(0, Error::OUT_OF_MEMORY); + EMC_SEMAPHORE_TAKE(); + } else { + _setState(State::disconnectingMqtt2); } - break; - // all cases covered, no default case - } - EMC_YIELD(); -#if defined(ARDUINO_ARCH_ESP32) && ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_INFO - size_t waterMark = uxTaskGetStackHighWaterMark(NULL); - if (waterMark < _highWaterMark) { - _highWaterMark = waterMark; - emc_log_i("Stack usage: %zu/%i", EMC_TASK_STACK_SIZE - _highWaterMark, EMC_TASK_STACK_SIZE); - } -#endif + } + _checkOutbox(); + _checkIncoming(); + _checkPing(); + _checkTimeout(); + EMC_SEMAPHORE_GIVE(); + break; + case State::disconnectingTcp1: + _transport->stop(); + _setState(State::disconnectingTcp2); + break; // keep break to accomodate async clients + case State::disconnectingTcp2: + if (_transport->disconnected()) { + EMC_SEMAPHORE_TAKE(); + _clearQueue(0); + EMC_SEMAPHORE_GIVE(); + _bytesSent = 0; + _setState(State::disconnected); + if (_onDisconnectCallback) { + _onDisconnectCallback(_disconnectReason); + } + } + break; + // all cases covered, no default case + } + EMC_YIELD(); + #if defined(ARDUINO_ARCH_ESP32) && ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_INFO + size_t waterMark = uxTaskGetStackHighWaterMark(NULL); + if (waterMark < _highWaterMark) { + _highWaterMark = waterMark; + emc_log_i("Stack usage: %zu/%i", EMC_TASK_STACK_SIZE - _highWaterMark, EMC_TASK_STACK_SIZE); + } + #endif } #if defined(ARDUINO_ARCH_ESP32) -void MqttClient::_loop(MqttClient * c) { -#if EMC_USE_WATCHDOG - if (esp_task_wdt_add(NULL) != ESP_OK) { - emc_log_e("Failed to add async task to WDT"); - } -#endif - for (;;) { - c->loop(); -#if EMC_USE_WATCHDOG - esp_task_wdt_reset(); -#endif - } +void MqttClient::_loop(MqttClient* c) { + #if EMC_USE_WATCHDOG + if (esp_task_wdt_add(NULL) != ESP_OK) { + emc_log_e("Failed to add async task to WDT"); + } + #endif + for (;;) { + c->loop(); + #if EMC_USE_WATCHDOG + esp_task_wdt_reset(); + #endif + } } #endif +inline void MqttClient::_setState(State newState) { + emc_log_i("state %i --> %i", static_cast::type>(_state.load()), static_cast::type>(newState)); + _state = newState; +} + uint16_t MqttClient::_getNextPacketId() { - ++_packetId; - if (_packetId == 0) - ++_packetId; - return _packetId; + ++_packetId; + if (_packetId == 0) ++_packetId; + return _packetId; } void MqttClient::_checkOutbox() { - while (_sendPacket() > 0) { - if (!_advanceOutbox()) { - break; - } + while (_sendPacket() > 0) { + if (!_advanceOutbox()) { + break; } + } } int MqttClient::_sendPacket() { - EMC_SEMAPHORE_TAKE(); - OutgoingPacket * packet = _outbox.getCurrent(); + OutgoingPacket* packet = _outbox.getCurrent(); - size_t written = 0; - if (packet) { - size_t wantToWrite = packet->packet.available(_bytesSent); - if (wantToWrite == 0) { - EMC_SEMAPHORE_GIVE(); - return 0; - } - written = _transport->write(packet->packet.data(_bytesSent), wantToWrite); - packet->timeSent = millis(); - _lastClientActivity = millis(); - _bytesSent += written; - emc_log_i("tx %zu/%zu (%02x)", _bytesSent, packet->packet.size(), packet->packet.packetType()); + size_t written = 0; + if (packet) { + size_t wantToWrite = packet->packet.available(_bytesSent); + if (wantToWrite == 0) { + return 0; } - EMC_SEMAPHORE_GIVE(); - return written; + written = _transport->write(packet->packet.data(_bytesSent), wantToWrite); + packet->timeSent = millis(); + _lastClientActivity = millis(); + _bytesSent += written; + emc_log_i("tx %zu/%zu (%02x)", _bytesSent, packet->packet.size(), packet->packet.packetType()); + } + return written; } bool MqttClient::_advanceOutbox() { - EMC_SEMAPHORE_TAKE(); - OutgoingPacket * packet = _outbox.getCurrent(); - if (packet && _bytesSent == packet->packet.size()) { - if ((packet->packet.packetType()) == PacketType.DISCONNECT) { - _state = State::disconnectingTcp1; - _disconnectReason = DisconnectReason::USER_OK; - } - if (packet->packet.removable()) { - _outbox.removeCurrent(); - } else { - // we already set 'dup' here, in case we have to retry - if ((packet->packet.packetType()) == PacketType.PUBLISH) - packet->packet.setDup(); - _outbox.next(); - } - packet = _outbox.getCurrent(); - _bytesSent = 0; + OutgoingPacket* packet = _outbox.getCurrent(); + if (packet && _bytesSent == packet->packet.size()) { + if ((packet->packet.packetType()) == PacketType.DISCONNECT) { + _setState(State::disconnectingTcp1); + _disconnectReason = DisconnectReason::USER_OK; } - EMC_SEMAPHORE_GIVE(); - return packet; + if (packet->packet.removable()) { + _outbox.removeCurrent(); + } else { + // we already set 'dup' here, in case we have to retry + if ((packet->packet.packetType()) == PacketType.PUBLISH) packet->packet.setDup(); + _outbox.next(); + } + packet = _outbox.getCurrent(); + _bytesSent = 0; + } + return packet; } void MqttClient::_checkIncoming() { - int32_t remainingBufferLength = _transport->read(_rxBuffer, EMC_RX_BUFFER_SIZE); - if (remainingBufferLength > 0) { - _lastServerActivity = millis(); - emc_log_i("rx len %i", remainingBufferLength); - size_t bytesParsed = 0; - size_t index = 0; - while (remainingBufferLength > 0) { - espMqttClientInternals::ParserResult result = _parser.parse(&_rxBuffer[index], remainingBufferLength, &bytesParsed); - if (result == espMqttClientInternals::ParserResult::packet) { - espMqttClientInternals::MQTTPacketType packetType = _parser.getPacket().fixedHeader.packetType & 0xF0; - if (_state == State::connectingMqtt && packetType != PacketType.CONNACK) { - emc_log_w("Disconnecting, expected CONNACK - protocol error"); - _state = State::disconnectingTcp1; - return; - } - switch (packetType & 0xF0) { - case PacketType.CONNACK: - _onConnack(); - if (_state != State::connected) { - return; - } - break; - case PacketType.PUBLISH: - if (_state >= State::disconnectingMqtt1) - break; // stop processing incoming once user has called disconnect - _onPublish(); - break; - case PacketType.PUBACK: - _onPuback(); - break; - case PacketType.PUBREC: - _onPubrec(); - break; - case PacketType.PUBREL: - _onPubrel(); - break; - case PacketType.PUBCOMP: - _onPubcomp(); - break; - case PacketType.SUBACK: - _onSuback(); - break; - case PacketType.UNSUBACK: - _onUnsuback(); - break; - case PacketType.PINGRESP: - _pingSent = false; - break; - } - } else if (result == espMqttClientInternals::ParserResult::protocolError) { - emc_log_w("Disconnecting, protocol error"); - _state = State::disconnectingTcp1; - _disconnectReason = DisconnectReason::TCP_DISCONNECTED; - return; - } - remainingBufferLength -= bytesParsed; - index += bytesParsed; - emc_log_i("Parsed %zu - remaining %i", bytesParsed, remainingBufferLength); - bytesParsed = 0; + int32_t remainingBufferLength = _transport->read(_rxBuffer, EMC_RX_BUFFER_SIZE); + if (remainingBufferLength > 0) { + _lastServerActivity = millis(); + emc_log_i("rx len %i", remainingBufferLength); + size_t bytesParsed = 0; + size_t index = 0; + while (remainingBufferLength > 0) { + espMqttClientInternals::ParserResult result = _parser.parse(&_rxBuffer[index], remainingBufferLength, &bytesParsed); + if (result == espMqttClientInternals::ParserResult::packet) { + espMqttClientInternals::MQTTPacketType packetType = _parser.getPacket().fixedHeader.packetType & 0xF0; + if (_state == State::connectingMqtt && packetType != PacketType.CONNACK) { + emc_log_w("Disconnecting, expected CONNACK - protocol error"); + _setState(State::disconnectingTcp1); + return; } + switch (packetType) { + case PacketType.CONNACK: + _onConnack(); + if (_state != State::connected) { + return; + } + break; + case PacketType.PUBLISH: + if (_state >= State::disconnectingMqtt1) break; // stop processing incoming once user has called disconnect + _onPublish(); + break; + case PacketType.PUBACK: + _onPuback(); + break; + case PacketType.PUBREC: + _onPubrec(); + break; + case PacketType.PUBREL: + _onPubrel(); + break; + case PacketType.PUBCOMP: + _onPubcomp(); + break; + case PacketType.SUBACK: + _onSuback(); + break; + case PacketType.UNSUBACK: + _onUnsuback(); + break; + case PacketType.PINGRESP: + _pingSent = false; + break; + } + } else if (result == espMqttClientInternals::ParserResult::protocolError) { + emc_log_w("Disconnecting, protocol error"); + _setState(State::disconnectingTcp1); + _disconnectReason = DisconnectReason::TCP_DISCONNECTED; + return; + } + remainingBufferLength -= bytesParsed; + index += bytesParsed; + emc_log_i("Parsed %zu - remaining %i", bytesParsed, remainingBufferLength); + bytesParsed = 0; } + } } void MqttClient::_checkPing() { - if (_keepAlive == 0) - return; // keepalive is disabled + if (_keepAlive == 0) return; // keepalive is disabled - uint32_t currentMillis = millis(); + uint32_t currentMillis = millis(); - // disconnect when server was inactive for twice the keepalive time - if (currentMillis - _lastServerActivity > 2 * _keepAlive) { - emc_log_w("Disconnecting, server exceeded keepalive"); - _state = State::disconnectingTcp1; - _disconnectReason = DisconnectReason::TCP_DISCONNECTED; - return; - } - - // send ping when client was inactive during the keepalive time - // or when server hasn't responded within keepalive time (typically due to QOS 0) - if (!_pingSent && ((currentMillis - _lastClientActivity > _keepAlive) || (currentMillis - _lastServerActivity > _keepAlive))) { - EMC_SEMAPHORE_TAKE(); - if (!_addPacket(PacketType.PINGREQ)) { - EMC_SEMAPHORE_GIVE(); - emc_log_e("Could not create PING packet"); - return; - } - EMC_SEMAPHORE_GIVE(); - _pingSent = true; + // disconnect when server was inactive for twice the keepalive time + if (currentMillis - _lastServerActivity > 2 * _keepAlive) { + emc_log_w("Disconnecting, server exceeded keepalive"); + _setState(State::disconnectingTcp1); + _disconnectReason = DisconnectReason::TCP_DISCONNECTED; + return; + } + + // send ping when client was inactive during the keepalive time + // or when server hasn't responded within keepalive time (typically due to QOS 0) + if (!_pingSent && + ((currentMillis - _lastClientActivity > _keepAlive) || + (currentMillis - _lastServerActivity > _keepAlive))) { + if (!_addPacket(PacketType.PINGREQ)) { + emc_log_e("Could not create PING packet"); + return; } + _pingSent = true; + } } void MqttClient::_checkTimeout() { - EMC_SEMAPHORE_TAKE(); - espMqttClientInternals::Outbox::Iterator it = _outbox.front(); - // check that we're not busy sending - // don't check when first item hasn't been sent yet - if (it && _bytesSent == 0 && it.get() != _outbox.getCurrent()) { - if (millis() - it.get()->timeSent > _timeout) { - emc_log_w("Packet ack timeout, retrying"); - _outbox.resetCurrent(); - } + espMqttClientInternals::Outbox::Iterator it = _outbox.front(); + // check that we're not busy sending + // don't check when first item hasn't been sent yet + if (it && _bytesSent == 0 && it.get() != _outbox.getCurrent()) { + if (millis() - it.get()->timeSent > _timeout) { + emc_log_w("Packet ack timeout, retrying"); + _outbox.resetCurrent(); } - EMC_SEMAPHORE_GIVE(); + } } void MqttClient::_onConnack() { - if (_parser.getPacket().variableHeader.fixed.connackVarHeader.returnCode == 0x00) { - _pingSent = false; // reset after keepalive timeout disconnect - _state = State::connected; - _advanceOutbox(); - if (_parser.getPacket().variableHeader.fixed.connackVarHeader.sessionPresent == 0) { - _clearQueue(1); - } - if (_onConnectCallback) { - _onConnectCallback(_parser.getPacket().variableHeader.fixed.connackVarHeader.sessionPresent); - } - } else { - _state = State::disconnectingTcp1; - // cast is safe because the parser already checked for a valid return code - _disconnectReason = static_cast(_parser.getPacket().variableHeader.fixed.connackVarHeader.returnCode); + if (_parser.getPacket().variableHeader.fixed.connackVarHeader.returnCode == 0x00) { + _pingSent = false; // reset after keepalive timeout disconnect + _setState(State::connected); + _advanceOutbox(); + if (_parser.getPacket().variableHeader.fixed.connackVarHeader.sessionPresent == 0) { + _clearQueue(1); } + if (_onConnectCallback) { + EMC_SEMAPHORE_GIVE(); + _onConnectCallback(_parser.getPacket().variableHeader.fixed.connackVarHeader.sessionPresent); + EMC_SEMAPHORE_TAKE(); + } + } else { + _setState(State::disconnectingTcp1); + // cast is safe because the parser already checked for a valid return code + _disconnectReason = static_cast(_parser.getPacket().variableHeader.fixed.connackVarHeader.returnCode); + } } void MqttClient::_onPublish() { - const espMqttClientInternals::IncomingPacket & p = _parser.getPacket(); - uint8_t qos = p.qos(); - bool retain = p.retain(); - bool dup = p.dup(); - uint16_t packetId = p.variableHeader.fixed.packetId; - bool callback = true; - if (qos == 1) { - if (p.payload.index + p.payload.length == p.payload.total) { - EMC_SEMAPHORE_TAKE(); - if (!_addPacket(PacketType.PUBACK, packetId)) { - emc_log_e("Could not create PUBACK packet"); - } - EMC_SEMAPHORE_GIVE(); - } - } else if (qos == 2) { - EMC_SEMAPHORE_TAKE(); - espMqttClientInternals::Outbox::Iterator it = _outbox.front(); - while (it) { - if ((it.get()->packet.packetType()) == PacketType.PUBREC && it.get()->packet.packetId() == packetId) { - callback = false; - _outbox.remove(it); - emc_log_e("QoS2 packet previously delivered"); - break; - } - ++it; - } - if (p.payload.index + p.payload.length == p.payload.total) { - if (!_addPacket(PacketType.PUBREC, packetId)) { - emc_log_e("Could not create PUBREC packet"); - } - } - EMC_SEMAPHORE_GIVE(); + const espMqttClientInternals::IncomingPacket& p = _parser.getPacket(); + uint8_t qos = p.qos(); + bool retain = p.retain(); + bool dup = p.dup(); + uint16_t packetId = p.variableHeader.fixed.packetId; + bool callback = true; + if (qos == 1) { + if (p.payload.index + p.payload.length == p.payload.total) { + if (!_addPacket(PacketType.PUBACK, packetId)) { + emc_log_e("Could not create PUBACK packet"); + } } - if (callback && _onMessageCallback) - _onMessageCallback({qos, dup, retain, packetId}, p.variableHeader.topic, p.payload.data, p.payload.length, p.payload.index, p.payload.total); + } else if (qos == 2) { + espMqttClientInternals::Outbox::Iterator it = _outbox.front(); + while (it) { + if ((it.get()->packet.packetType()) == PacketType.PUBREC && it.get()->packet.packetId() == packetId) { + callback = false; + emc_log_e("QoS2 packet previously delivered"); + break; + } + ++it; + } + if (p.payload.index + p.payload.length == p.payload.total) { + if (!_addPacket(PacketType.PUBREC, packetId)) { + emc_log_e("Could not create PUBREC packet"); + } + } + } + if (callback && _onMessageCallback) { + EMC_SEMAPHORE_GIVE(); + _onMessageCallback({qos, dup, retain, packetId}, + p.variableHeader.topic, + p.payload.data, + p.payload.length, + p.payload.index, + p.payload.total); + EMC_SEMAPHORE_TAKE(); + } } void MqttClient::_onPuback() { - bool callback = false; - uint16_t idToMatch = _parser.getPacket().variableHeader.fixed.packetId; - EMC_SEMAPHORE_TAKE(); - espMqttClientInternals::Outbox::Iterator it = _outbox.front(); - while (it) { - // PUBACKs come in the order PUBs are sent. So we only check the first PUB packet in outbox - // if it doesn't match the ID, return - if ((it.get()->packet.packetType()) == PacketType.PUBLISH) { - if (it.get()->packet.packetId() == idToMatch) { - callback = true; - _outbox.remove(it); - break; - } - emc_log_w("Received out of order PUBACK"); - break; - } - ++it; + bool callback = false; + uint16_t idToMatch = _parser.getPacket().variableHeader.fixed.packetId; + espMqttClientInternals::Outbox::Iterator it = _outbox.front(); + while (it) { + // PUBACKs come in the order PUBs are sent. So we only check the first PUB packet in outbox + // if it doesn't match the ID, return + if ((it.get()->packet.packetType()) == PacketType.PUBLISH) { + if (it.get()->packet.packetId() == idToMatch) { + callback = true; + _outbox.remove(it); + break; + } + emc_log_w("Received out of order PUBACK"); + break; } - EMC_SEMAPHORE_GIVE(); - if (callback) { - if (_onPublishCallback) - _onPublishCallback(idToMatch); - } else { - emc_log_w("No matching PUBLISH packet found"); + ++it; + } + if (callback) { + if (_onPublishCallback) { + EMC_SEMAPHORE_GIVE(); + _onPublishCallback(idToMatch); + EMC_SEMAPHORE_TAKE(); } + } else { + emc_log_w("No matching PUBLISH packet found"); + } } void MqttClient::_onPubrec() { - bool success = false; - uint16_t idToMatch = _parser.getPacket().variableHeader.fixed.packetId; - EMC_SEMAPHORE_TAKE(); - espMqttClientInternals::Outbox::Iterator it = _outbox.front(); - while (it) { - // PUBRECs come in the order PUBs are sent. So we only check the first PUB packet in outbox - // if it doesn't match the ID, return - if ((it.get()->packet.packetType()) == PacketType.PUBLISH || (it.get()->packet.packetType()) == PacketType.PUBREL) { - if (it.get()->packet.packetId() == idToMatch) { - if (!_addPacket(PacketType.PUBREL, idToMatch)) { - emc_log_e("Could not create PUBREL packet"); - } - _outbox.remove(it); - success = true; - break; - } - emc_log_w("Received out of order PUBREC"); - break; + bool success = false; + uint16_t idToMatch = _parser.getPacket().variableHeader.fixed.packetId; + espMqttClientInternals::Outbox::Iterator it = _outbox.front(); + while (it) { + // PUBRECs come in the order PUBs are sent. So we only check the first PUB packet in outbox + // if it doesn't match the ID, return + if ((it.get()->packet.packetType()) == PacketType.PUBLISH) { + if (it.get()->packet.packetId() == idToMatch) { + if (!_addPacket(PacketType.PUBREL, idToMatch)) { + emc_log_e("Could not create PUBREL packet"); } - ++it; + _outbox.remove(it); + success = true; + break; + } + emc_log_w("Received out of order PUBREC"); + break; } - if (!success) { - emc_log_w("No matching PUBLISH packet found"); - } - EMC_SEMAPHORE_GIVE(); + ++it; + } + if (!success) { + emc_log_w("No matching PUBLISH packet found"); + } } void MqttClient::_onPubrel() { - bool success = false; - uint16_t idToMatch = _parser.getPacket().variableHeader.fixed.packetId; - EMC_SEMAPHORE_TAKE(); - espMqttClientInternals::Outbox::Iterator it = _outbox.front(); - while (it) { - // PUBRELs come in the order PUBRECs are sent. So we only check the first PUBREC packet in outbox - // if it doesn't match the ID, return - if ((it.get()->packet.packetType()) == PacketType.PUBREC) { - if (it.get()->packet.packetId() == idToMatch) { - if (!_addPacket(PacketType.PUBCOMP, idToMatch)) { - emc_log_e("Could not create PUBCOMP packet"); - } - _outbox.remove(it); - success = true; - break; - } - emc_log_w("Received out of order PUBREL"); - break; + bool success = false; + uint16_t idToMatch = _parser.getPacket().variableHeader.fixed.packetId; + espMqttClientInternals::Outbox::Iterator it = _outbox.front(); + while (it) { + // PUBRELs come in the order PUBRECs are sent. So we only check the first PUBREC packet in outbox + // if it doesn't match the ID, return + if ((it.get()->packet.packetType()) == PacketType.PUBREC) { + if (it.get()->packet.packetId() == idToMatch) { + if (!_addPacket(PacketType.PUBCOMP, idToMatch)) { + emc_log_e("Could not create PUBCOMP packet"); } - ++it; + _outbox.remove(it); + success = true; + break; + } + emc_log_w("Received out of order PUBREL"); + break; } - if (!success) { - emc_log_w("No matching PUBREC packet found"); - } - EMC_SEMAPHORE_GIVE(); + ++it; + } + if (!success) { + emc_log_w("No matching PUBREC packet found"); + } } void MqttClient::_onPubcomp() { - bool callback = false; - EMC_SEMAPHORE_TAKE(); - espMqttClientInternals::Outbox::Iterator it = _outbox.front(); - uint16_t idToMatch = _parser.getPacket().variableHeader.fixed.packetId; - while (it) { - // PUBCOMPs come in the order PUBRELs are sent. So we only check the first PUBREL packet in outbox - // if it doesn't match the ID, return - if ((it.get()->packet.packetType()) == PacketType.PUBREL) { - if (it.get()->packet.packetId() == idToMatch) { - callback = true; - _outbox.remove(it); - break; - } - emc_log_w("Received out of order PUBCOMP"); - break; - } - ++it; + bool callback = false; + espMqttClientInternals::Outbox::Iterator it = _outbox.front(); + uint16_t idToMatch = _parser.getPacket().variableHeader.fixed.packetId; + while (it) { + // PUBCOMPs come in the order PUBRELs are sent. So we only check the first PUBREL packet in outbox + // if it doesn't match the ID, return + if ((it.get()->packet.packetType()) == PacketType.PUBREL) { + if (it.get()->packet.packetId() == idToMatch) { + callback = true; + _outbox.remove(it); + break; + } + emc_log_w("Received out of order PUBCOMP"); + break; } - EMC_SEMAPHORE_GIVE(); - if (callback) { - if (_onPublishCallback) - _onPublishCallback(idToMatch); - } else { - emc_log_w("No matching PUBREL packet found"); + ++it; + } + if (callback) { + if (_onPublishCallback) { + EMC_SEMAPHORE_GIVE(); + _onPublishCallback(idToMatch); + EMC_SEMAPHORE_TAKE(); } + } else { + emc_log_w("No matching PUBREL packet found"); + } } void MqttClient::_onSuback() { - bool callback = false; - uint16_t idToMatch = _parser.getPacket().variableHeader.fixed.packetId; - EMC_SEMAPHORE_TAKE(); - espMqttClientInternals::Outbox::Iterator it = _outbox.front(); - while (it) { - if (((it.get()->packet.packetType()) == PacketType.SUBSCRIBE) && it.get()->packet.packetId() == idToMatch) { - callback = true; - _outbox.remove(it); - break; - } - ++it; + bool callback = false; + uint16_t idToMatch = _parser.getPacket().variableHeader.fixed.packetId; + espMqttClientInternals::Outbox::Iterator it = _outbox.front(); + while (it) { + if (((it.get()->packet.packetType()) == PacketType.SUBSCRIBE) && it.get()->packet.packetId() == idToMatch) { + callback = true; + _outbox.remove(it); + break; } - EMC_SEMAPHORE_GIVE(); - if (callback) { - if (_onSubscribeCallback) - _onSubscribeCallback(idToMatch, - reinterpret_cast(_parser.getPacket().payload.data), - _parser.getPacket().payload.total); - } else { - emc_log_w("received SUBACK without SUB"); + ++it; + } + if (callback) { + if (_onSubscribeCallback) { + EMC_SEMAPHORE_GIVE(); + _onSubscribeCallback(idToMatch, reinterpret_cast(_parser.getPacket().payload.data), _parser.getPacket().payload.total); + EMC_SEMAPHORE_TAKE(); } + } else { + emc_log_w("received SUBACK without SUB"); + } } void MqttClient::_onUnsuback() { - bool callback = false; - EMC_SEMAPHORE_TAKE(); - espMqttClientInternals::Outbox::Iterator it = _outbox.front(); - uint16_t idToMatch = _parser.getPacket().variableHeader.fixed.packetId; - while (it) { - if (it.get()->packet.packetId() == idToMatch) { - callback = true; - _outbox.remove(it); - break; - } - ++it; + bool callback = false; + espMqttClientInternals::Outbox::Iterator it = _outbox.front(); + uint16_t idToMatch = _parser.getPacket().variableHeader.fixed.packetId; + while (it) { + if (it.get()->packet.packetId() == idToMatch) { + callback = true; + _outbox.remove(it); + break; } - EMC_SEMAPHORE_GIVE(); - if (callback) { - if (_onUnsubscribeCallback) - _onUnsubscribeCallback(idToMatch); - } else { - emc_log_w("received UNSUBACK without UNSUB"); + ++it; + } + if (callback) { + if (_onUnsubscribeCallback) { + EMC_SEMAPHORE_GIVE(); + _onUnsubscribeCallback(idToMatch); + EMC_SEMAPHORE_TAKE(); } + } else { + emc_log_w("received UNSUBACK without UNSUB"); + } } void MqttClient::_clearQueue(int clearData) { - emc_log_i("clearing queue (clear session: %d)", clearData); - EMC_SEMAPHORE_TAKE(); - espMqttClientInternals::Outbox::Iterator it = _outbox.front(); - if (clearData == 0) { - // keep PUB (qos > 0, aka packetID != 0), PUBREC and PUBREL - // Spec only mentions PUB and PUBREL but this lib implements method B from point 4.3.3 (Fig. 4.3) - // and stores the packet id in the PUBREC packet. So we also must keep PUBREC. - while (it) { - espMqttClientInternals::MQTTPacketType type = it.get()->packet.packetType(); - if (type == PacketType.PUBREC || type == PacketType.PUBREL || (type == PacketType.PUBLISH && it.get()->packet.packetId() != 0)) { - ++it; - } else { - _outbox.remove(it); - } - } - } else if (clearData == 1) { - // keep PUB - while (it) { - if (it.get()->packet.packetType() == PacketType.PUBLISH) { - ++it; - } else { - _outbox.remove(it); - } - } - } else { // clearData == 2 - while (it) { - _outbox.remove(it); - } + emc_log_i("clearing queue (clear session: %d)", clearData); + espMqttClientInternals::Outbox::Iterator it = _outbox.front(); + if (clearData == 0) { + // keep PUB (qos > 0, aka packetID != 0), PUBREC and PUBREL + // Spec only mentions PUB and PUBREL but this lib implements method B from point 4.3.3 (Fig. 4.3) + // and stores the packet id in the PUBREC packet. So we also must keep PUBREC. + while (it) { + espMqttClientInternals::MQTTPacketType type = it.get()->packet.packetType(); + if (type == PacketType.PUBREC || + type == PacketType.PUBREL || + (type == PacketType.PUBLISH && it.get()->packet.packetId() != 0)) { + ++it; + } else { + _outbox.remove(it); + } } - EMC_SEMAPHORE_GIVE(); + } else if (clearData == 1) { + // keep PUB + while (it) { + if (it.get()->packet.packetType() == PacketType.PUBLISH) { + ++it; + } else { + _outbox.remove(it); + } + } + } else { // clearData == 2 + while (it) { + _outbox.remove(it); + } + } } void MqttClient::_onError(uint16_t packetId, espMqttClientTypes::Error error) { - if (_onErrorCallback) { - _onErrorCallback(packetId, error); - } + if (_onErrorCallback) { + _onErrorCallback(packetId, error); + } } diff --git a/lib/espMqttClient/src/MqttClient.h b/lib/espMqttClient/src/MqttClient.h index dba4bf245..eaf9d2d79 100644 --- a/lib/espMqttClient/src/MqttClient.h +++ b/lib/espMqttClient/src/MqttClient.h @@ -24,179 +24,178 @@ the LICENSE file. #include "Transport/Transport.h" class MqttClient { - public: - virtual ~MqttClient(); - bool connected() const; - bool disconnected() const; - bool connect(); - bool disconnect(bool force = false); - template - uint16_t subscribe(const char * topic, uint8_t qos, Args &&... args) { - uint16_t packetId = _getNextPacketId(); - if (_state != State::connected) { - packetId = 0; - } else { - EMC_SEMAPHORE_TAKE(); - if (!_addPacket(packetId, topic, qos, std::forward(args)...)) { - emc_log_e("Could not create SUBSCRIBE packet"); - packetId = 0; - } - EMC_SEMAPHORE_GIVE(); - } - return packetId; + public: + virtual ~MqttClient(); + bool connected() const; + bool disconnected() const; + bool connect(); + bool disconnect(bool force = false); + template + uint16_t subscribe(const char* topic, uint8_t qos, Args&&... args) { + uint16_t packetId = 0; + if (_state != State::connected) { + return packetId; + } else { + EMC_SEMAPHORE_TAKE(); + packetId = _getNextPacketId(); + if (!_addPacket(packetId, topic, qos, std::forward(args) ...)) { + emc_log_e("Could not create SUBSCRIBE packet"); + packetId = 0; + } + EMC_SEMAPHORE_GIVE(); } - template - uint16_t unsubscribe(const char * topic, Args &&... args) { - uint16_t packetId = _getNextPacketId(); - if (_state != State::connected) { - packetId = 0; - } else { - EMC_SEMAPHORE_TAKE(); - if (!_addPacket(packetId, topic, std::forward(args)...)) { - emc_log_e("Could not create UNSUBSCRIBE packet"); - packetId = 0; - } - EMC_SEMAPHORE_GIVE(); - } - return packetId; + return packetId; + } + template + uint16_t unsubscribe(const char* topic, Args&&... args) { + uint16_t packetId = 0; + if (_state != State::connected) { + return packetId; + } else { + EMC_SEMAPHORE_TAKE(); + packetId = _getNextPacketId(); + if (!_addPacket(packetId, topic, std::forward(args) ...)) { + emc_log_e("Could not create UNSUBSCRIBE packet"); + packetId = 0; + } + EMC_SEMAPHORE_GIVE(); } - uint16_t publish(const char * topic, uint8_t qos, bool retain, const uint8_t * payload, size_t length); - uint16_t publish(const char * topic, uint8_t qos, bool retain, const char * payload); - uint16_t publish(const char * topic, uint8_t qos, bool retain, espMqttClientTypes::PayloadCallback callback, size_t length); - void clearQueue(bool deleteSessionData = false); // Not MQTT compliant and may cause unpredictable results when `deleteSessionData` = true! - const char * getClientId() const; - size_t queueSize(); // No const because of mutex - void loop(); + return packetId; + } + uint16_t publish(const char* topic, uint8_t qos, bool retain, const uint8_t* payload, size_t length); + uint16_t publish(const char* topic, uint8_t qos, bool retain, const char* payload); + uint16_t publish(const char* topic, uint8_t qos, bool retain, espMqttClientTypes::PayloadCallback callback, size_t length); + void clearQueue(bool deleteSessionData = false); // Not MQTT compliant and may cause unpredictable results when `deleteSessionData` = true! + const char* getClientId() const; + size_t queueSize(); // No const because of mutex + void loop(); - protected: - explicit MqttClient(espMqttClientTypes::UseInternalTask useInternalTask, uint8_t priority = 1, uint8_t core = 1); - espMqttClientTypes::UseInternalTask _useInternalTask; - espMqttClientInternals::Transport * _transport; + protected: + explicit MqttClient(espMqttClientTypes::UseInternalTask useInternalTask, uint8_t priority = 1, uint8_t core = 1); + espMqttClientTypes::UseInternalTask _useInternalTask; + espMqttClientInternals::Transport* _transport; - espMqttClientTypes::OnConnectCallback _onConnectCallback; - espMqttClientTypes::OnDisconnectCallback _onDisconnectCallback; - espMqttClientTypes::OnSubscribeCallback _onSubscribeCallback; - espMqttClientTypes::OnUnsubscribeCallback _onUnsubscribeCallback; - espMqttClientTypes::OnMessageCallback _onMessageCallback; - espMqttClientTypes::OnPublishCallback _onPublishCallback; - espMqttClientTypes::OnErrorCallback _onErrorCallback; - typedef void (*mqttClientHook)(void *); - const char * _clientId; - IPAddress _ip; - const char * _host; - uint16_t _port; - bool _useIp; - uint32_t _keepAlive; - bool _cleanSession; - const char * _username; - const char * _password; - const char * _willTopic; - const uint8_t * _willPayload; - uint16_t _willPayloadLength; - uint8_t _willQos; - bool _willRetain; - uint32_t _timeout; + espMqttClientTypes::OnConnectCallback _onConnectCallback; + espMqttClientTypes::OnDisconnectCallback _onDisconnectCallback; + espMqttClientTypes::OnSubscribeCallback _onSubscribeCallback; + espMqttClientTypes::OnUnsubscribeCallback _onUnsubscribeCallback; + espMqttClientTypes::OnMessageCallback _onMessageCallback; + espMqttClientTypes::OnPublishCallback _onPublishCallback; + espMqttClientTypes::OnErrorCallback _onErrorCallback; + typedef void(*mqttClientHook)(void*); + const char* _clientId; + IPAddress _ip; + const char* _host; + uint16_t _port; + bool _useIp; + uint32_t _keepAlive; + bool _cleanSession; + const char* _username; + const char* _password; + const char* _willTopic; + const uint8_t* _willPayload; + uint16_t _willPayloadLength; + uint8_t _willQos; + bool _willRetain; + uint32_t _timeout; - // state is protected to allow state changes by the transport system, defined in child classes - // eg. to allow AsyncTCP - enum class State { - disconnected = 0, - connectingTcp1 = 1, - connectingTcp2 = 2, - connectingMqtt = 3, - connected = 4, - disconnectingMqtt1 = 5, - disconnectingMqtt2 = 6, - disconnectingTcp1 = 7, - disconnectingTcp2 = 8 - }; - std::atomic _state; + // state is protected to allow state changes by the transport system, defined in child classes + // eg. to allow AsyncTCP + enum class State { + disconnected = 0, + connectingTcp1 = 1, + connectingTcp2 = 2, + connectingMqtt = 3, + connected = 4, + disconnectingMqtt1 = 5, + disconnectingMqtt2 = 6, + disconnectingTcp1 = 7, + disconnectingTcp2 = 8 + }; + std::atomic _state; + inline void _setState(State newState); - private: - char _generatedClientId[EMC_CLIENTID_LENGTH]; - uint16_t _packetId; + private: + char _generatedClientId[EMC_CLIENTID_LENGTH]; + uint16_t _packetId; #if defined(ARDUINO_ARCH_ESP32) - SemaphoreHandle_t _xSemaphore; - TaskHandle_t _taskHandle; - static void _loop(MqttClient * c); + SemaphoreHandle_t _xSemaphore; + TaskHandle_t _taskHandle; + static void _loop(MqttClient* c); #elif defined(ARDUINO_ARCH_ESP8266) && EMC_ESP8266_MULTITHREADING - std::atomic _xSemaphore = false; + std::atomic _xSemaphore = false; #elif defined(__linux__) - mutable std::mutex mtx; // modified by proddy for EMS-ESP compiling standalone + std::mutex mtx; #endif - uint8_t _rxBuffer[EMC_RX_BUFFER_SIZE]; - struct OutgoingPacket { - uint32_t timeSent; - espMqttClientInternals::Packet packet; - template - OutgoingPacket(uint32_t t, espMqttClientTypes::Error & error, Args &&... args) - : // NOLINT(runtime/references) - timeSent(t) - , packet(error, std::forward(args)...) { - } - }; - espMqttClientInternals::Outbox _outbox; - size_t _bytesSent; - espMqttClientInternals::Parser _parser; - uint32_t _lastClientActivity; - uint32_t _lastServerActivity; - bool _pingSent; - espMqttClientTypes::DisconnectReason _disconnectReason; - - uint16_t _getNextPacketId(); - + uint8_t _rxBuffer[EMC_RX_BUFFER_SIZE]; + struct OutgoingPacket { + uint32_t timeSent; + espMqttClientInternals::Packet packet; template - bool _addPacket(Args &&... args) { - espMqttClientTypes::Error error(espMqttClientTypes::Error::SUCCESS); - espMqttClientInternals::Outbox::Iterator it = _outbox.emplace(0, error, std::forward(args)...); - if (it && error == espMqttClientTypes::Error::SUCCESS) { - return true; - } else { - if (it) - _outbox.remove(it); - return false; - } + OutgoingPacket(uint32_t t, espMqttClientTypes::Error& error, Args&&... args) : // NOLINT(runtime/references) + timeSent(t), + packet(error, std::forward(args) ...) {} + }; + espMqttClientInternals::Outbox _outbox; + size_t _bytesSent; + espMqttClientInternals::Parser _parser; + uint32_t _lastClientActivity; + uint32_t _lastServerActivity; + bool _pingSent; + espMqttClientTypes::DisconnectReason _disconnectReason; + + uint16_t _getNextPacketId(); + + template + bool _addPacket(Args&&... args) { + espMqttClientTypes::Error error(espMqttClientTypes::Error::SUCCESS); + espMqttClientInternals::Outbox::Iterator it = _outbox.emplace(0, error, std::forward(args) ...); + if (it && error == espMqttClientTypes::Error::SUCCESS) { + return true; + } else { + if (it) _outbox.remove(it); + return false; } + } - template - bool _addPacketFront(Args &&... args) { - espMqttClientTypes::Error error(espMqttClientTypes::Error::SUCCESS); - espMqttClientInternals::Outbox::Iterator it = _outbox.emplaceFront(0, error, std::forward(args)...); - if (it && error == espMqttClientTypes::Error::SUCCESS) { - return true; - } else { - if (it) - _outbox.remove(it); - return false; - } + template + bool _addPacketFront(Args&&... args) { + espMqttClientTypes::Error error(espMqttClientTypes::Error::SUCCESS); + espMqttClientInternals::Outbox::Iterator it = _outbox.emplaceFront(0, error, std::forward(args) ...); + if (it && error == espMqttClientTypes::Error::SUCCESS) { + return true; + } else { + if (it) _outbox.remove(it); + return false; } + } - void _checkOutbox(); - int _sendPacket(); - bool _advanceOutbox(); - void _checkIncoming(); - void _checkPing(); - void _checkTimeout(); + void _checkOutbox(); + int _sendPacket(); + bool _advanceOutbox(); + void _checkIncoming(); + void _checkPing(); + void _checkTimeout(); - void _onConnack(); - void _onPublish(); - void _onPuback(); - void _onPubrec(); - void _onPubrel(); - void _onPubcomp(); - void _onSuback(); - void _onUnsuback(); + void _onConnack(); + void _onPublish(); + void _onPuback(); + void _onPubrec(); + void _onPubrel(); + void _onPubcomp(); + void _onSuback(); + void _onUnsuback(); - void _clearQueue(int clearData); // 0: keep session, - // 1: keep only PUBLISH qos > 0 - // 2: delete all - void _onError(uint16_t packetId, espMqttClientTypes::Error error); + void _clearQueue(int clearData); // 0: keep session, + // 1: keep only PUBLISH qos > 0 + // 2: delete all + void _onError(uint16_t packetId, espMqttClientTypes::Error error); -#if defined(ARDUINO_ARCH_ESP32) -#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_INFO - size_t _highWaterMark; -#endif -#endif + #if defined(ARDUINO_ARCH_ESP32) + #if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_INFO + size_t _highWaterMark; + #endif + #endif }; diff --git a/lib/espMqttClient/src/Outbox.h b/lib/espMqttClient/src/Outbox.h index cfb9f244d..4f9971c5f 100644 --- a/lib/espMqttClient/src/Outbox.h +++ b/lib/espMqttClient/src/Outbox.h @@ -1,217 +1,255 @@ - -/* -Copyright (c) 2022 Bert Melis. All rights reserved. - -This work is licensed under the terms of the MIT license. -For a copy, see or -the LICENSE file. -*/ - -#pragma once - -#include // new (std::nothrow) -#include // std::forward - -namespace espMqttClientInternals { - -/** - * @brief Singly linked queue with builtin non-invalidating forward iterator - * - * Queue items can only be emplaced, at front and back of the queue. - * Remove items using an iterator or the builtin iterator. - */ - -template -class Outbox { - public: - Outbox() - : _first(nullptr) - , _last(nullptr) - , _current(nullptr) - , _prev(nullptr) {} - ~Outbox() { - while (_first) { - Node* n = _first->next; - delete _first; - _first = n; - } - } - - struct Node { - public: - template - explicit Node(Args&&... args) - : data(std::forward(args) ...) - , next(nullptr) { - // empty - } - - T data; - Node* next; - }; - - class Iterator { - friend class Outbox; - public: - void operator++() { - if (_node) { - _prev = _node; - _node = _node->next; - } - } - - explicit operator bool() const { - if (_node) return true; - return false; - } - - T* get() const { - if (_node) return &(_node->data); - return nullptr; - } - - private: - Node* _node = nullptr; - Node* _prev = nullptr; - }; - - // add node to back, advance current to new if applicable - template - Iterator emplace(Args&&... args) { - Iterator it; - Node* node = new (std::nothrow) Node(std::forward(args) ...); - if (node != nullptr) { - if (!_first) { - // queue is empty - _first = _current = node; - } else { - // queue has at least one item - _last->next = node; - it._prev = _last; - } - _last = node; - it._node = node; - // point current to newly created if applicable - if (!_current) { - _current = _last; - } - } - return it; - } - - // add item to front, current points to newly created front. - template - Iterator emplaceFront(Args&&... args) { - Iterator it; - Node* node = new (std::nothrow) Node(std::forward(args) ...); - if (node != nullptr) { - if (!_first) { - // queue is empty - _last = node; - } else { - // queue has at least one item - node->next = _first; - } - _current = _first = node; - _prev = nullptr; - it._node = node; - } - return it; - } - - // remove node at iterator, iterator points to next - void remove(Iterator& it) { // NOLINT(runtime/references) - if (!it) return; - Node* node = it._node; - Node* prev = it._prev; - ++it; - _remove(prev, node); - } - - // remove current node, current points to next - void removeCurrent() { - _remove(_prev, _current); - } - - // Get current item or return nullptr - T* getCurrent() const { - if (_current) return &(_current->data); - return nullptr; - } - - void resetCurrent() { - _current = _first; - } - - Iterator front() const { - Iterator it; - it._node = _first; - return it; - } - - // Advance current item - void next() { - if (_current) { - _prev = _current; - _current = _current->next; - } - } - - // Outbox is empty - bool empty() { - if (!_first) return true; - return false; - } - - size_t size() const { - Node* n = _first; - size_t count = 0; - while (n) { - n = n->next; - ++count; - } - return count; - } - - private: - Node* _first; - Node* _last; - Node* _current; - Node* _prev; // element just before _current - - void _remove(Node* prev, Node* node) { - if (!node) return; - - // set current to next, node->next may be nullptr - if (_current == node) { - _current = node->next; - } - - if (_prev == node) { - _prev = prev; - } - - // only one element in outbox - if (_first == _last) { - _first = _last = nullptr; - - // delete first el in longer outbox - } else if (_first == node) { - _first = node->next; - - // delete last in longer outbox - } else if (_last == node) { - _last = prev; - _last->next = nullptr; - - // delete somewhere in the middle - } else { - prev->next = node->next; - } - - // finally, delete the node - delete node; - } -}; - -} // end namespace espMqttClientInternals + +/* +Copyright (c) 2022 Bert Melis. All rights reserved. + +This work is licensed under the terms of the MIT license. +For a copy, see or +the LICENSE file. +*/ + +#pragma once + +#if EMC_USE_MEMPOOL + #include "MemoryPool/src/MemoryPool.h" + #include "Config.h" +#else + #include // new (std::nothrow) +#endif +#include // std::forward + +namespace espMqttClientInternals { + +/** + * @brief Singly linked queue with builtin non-invalidating forward iterator + * + * Queue items can only be emplaced, at front and back of the queue. + * Remove items using an iterator or the builtin iterator. + */ + +template +class Outbox { + public: + Outbox() + : _first(nullptr) + , _last(nullptr) + , _current(nullptr) + , _prev(nullptr) + #if EMC_USE_MEMPOOL + , _memPool() + #endif + {} + ~Outbox() { + while (_first) { + Node* n = _first->next; + #if EMC_USE_MEMPOOL + _first->~Node(); + _memPool.free(_first); + #else + delete _first; + #endif + _first = n; + } + } + + struct Node { + public: + template + explicit Node(Args&&... args) + : data(std::forward(args) ...) + , next(nullptr) { + // empty + } + + T data; + Node* next; + }; + + class Iterator { + friend class Outbox; + public: + void operator++() { + if (_node) { + _prev = _node; + _node = _node->next; + } + } + + explicit operator bool() const { + if (_node) return true; + return false; + } + + T* get() const { + if (_node) return &(_node->data); + return nullptr; + } + + private: + Node* _node = nullptr; + Node* _prev = nullptr; + }; + + // add node to back, advance current to new if applicable + template + Iterator emplace(Args&&... args) { + Iterator it; + #if EMC_USE_MEMPOOL + void* buf = _memPool.malloc(); + Node* node = nullptr; + if (buf) { + node = new(buf) Node(std::forward(args) ...); + } + #else + Node* node = new(std::nothrow) Node(std::forward(args) ...); + #endif + if (node != nullptr) { + if (!_first) { + // queue is empty + _first = _current = node; + } else { + // queue has at least one item + _last->next = node; + it._prev = _last; + } + _last = node; + it._node = node; + // point current to newly created if applicable + if (!_current) { + _current = _last; + } + } + return it; + } + + // add item to front, current points to newly created front. + template + Iterator emplaceFront(Args&&... args) { + Iterator it; + #if EMC_USE_MEMPOOL + void* buf = _memPool.malloc(); + Node* node = nullptr; + if (buf) { + node = new(buf) Node(std::forward(args) ...); + } + #else + Node* node = new(std::nothrow) Node(std::forward(args) ...); + #endif + if (node != nullptr) { + if (!_first) { + // queue is empty + _last = node; + } else { + // queue has at least one item + node->next = _first; + } + _current = _first = node; + _prev = nullptr; + it._node = node; + } + return it; + } + + // remove node at iterator, iterator points to next + void remove(Iterator& it) { // NOLINT(runtime/references) + if (!it) return; + Node* node = it._node; + Node* prev = it._prev; + ++it; + _remove(prev, node); + } + + // remove current node, current points to next + void removeCurrent() { + _remove(_prev, _current); + } + + // Get current item or return nullptr + T* getCurrent() const { + if (_current) return &(_current->data); + return nullptr; + } + + void resetCurrent() { + _current = _first; + } + + Iterator front() const { + Iterator it; + it._node = _first; + return it; + } + + // Advance current item + void next() { + if (_current) { + _prev = _current; + _current = _current->next; + } + } + + // Outbox is empty + bool empty() { + if (!_first) return true; + return false; + } + + size_t size() const { + Node* n = _first; + size_t count = 0; + while (n) { + n = n->next; + ++count; + } + return count; + } + + private: + Node* _first; + Node* _last; + Node* _current; + Node* _prev; // element just before _current + #if EMC_USE_MEMPOOL + MemoryPool::Fixed _memPool; + #endif + + void _remove(Node* prev, Node* node) { + if (!node) return; + + // set current to next, node->next may be nullptr + if (_current == node) { + _current = node->next; + } + + if (_prev == node) { + _prev = prev; + } + + // only one element in outbox + if (_first == _last) { + _first = _last = nullptr; + + // delete first el in longer outbox + } else if (_first == node) { + _first = node->next; + + // delete last in longer outbox + } else if (_last == node) { + _last = prev; + _last->next = nullptr; + + // delete somewhere in the middle + } else { + prev->next = node->next; + } + + // finally, delete the node + #if EMC_USE_MEMPOOL + node->~Node(); + _memPool.free(node); + #else + delete node; + #endif + } +}; + +} // end namespace espMqttClientInternals diff --git a/lib/espMqttClient/src/Packets/Packet.cpp b/lib/espMqttClient/src/Packets/Packet.cpp index 5f9ceb486..14d241b20 100644 --- a/lib/espMqttClient/src/Packets/Packet.cpp +++ b/lib/espMqttClient/src/Packets/Packet.cpp @@ -10,427 +10,445 @@ the LICENSE file. namespace espMqttClientInternals { +#if EMC_USE_MEMPOOL +MemoryPool::Variable Packet::_memPool; +#endif + Packet::~Packet() { - free(_data); + #if EMC_USE_MEMPOOL + _memPool.free(_data); + #else + free(_data); + #endif } size_t Packet::available(size_t index) { - if (index >= _size) - return 0; - if (!_getPayload) - return _size - index; - return _chunkedAvailable(index); + if (index >= _size) return 0; + if (!_getPayload) return _size - index; + return _chunkedAvailable(index); } -const uint8_t * Packet::data(size_t index) const { - if (!_getPayload) { - if (!_data) - return nullptr; - if (index >= _size) - return nullptr; - return &_data[index]; - } - return _chunkedData(index); +const uint8_t* Packet::data(size_t index) const { + if (!_getPayload) { + if (!_data) return nullptr; + if (index >= _size) return nullptr; + return &_data[index]; + } + return _chunkedData(index); } size_t Packet::size() const { - return _size; + return _size; } void Packet::setDup() { - if (!_data) - return; - if (packetType() != PacketType.PUBLISH) - return; - if (_packetId == 0) - return; - _data[0] |= 0x08; + if (!_data) return; + if (packetType() != PacketType.PUBLISH) return; + if (_packetId == 0) return; + _data[0] |= 0x08; } uint16_t Packet::packetId() const { - return _packetId; + return _packetId; } MQTTPacketType Packet::packetType() const { - if (_data) - return static_cast(_data[0] & 0xF0); - return static_cast(0); + if (_data) return static_cast(_data[0] & 0xF0); + return static_cast(0); } bool Packet::removable() const { - if (_packetId == 0) - return true; - if ((packetType() == PacketType.PUBACK) || (packetType() == PacketType.PUBCOMP)) - return true; - return false; + if (_packetId == 0) return true; + if ((packetType() == PacketType.PUBACK) || (packetType() == PacketType.PUBCOMP)) return true; + return false; } -Packet::Packet(espMqttClientTypes::Error & error, - bool cleanSession, - const char * username, - const char * password, - const char * willTopic, - bool willRetain, - uint8_t willQos, - const uint8_t * willPayload, - uint16_t willPayloadLength, - uint16_t keepAlive, - const char * clientId) - : _packetId(0) - , _data(nullptr) - , _size(0) - , _payloadIndex(0) - , _payloadStartIndex(0) - , _payloadEndIndex(0) - , _getPayload(nullptr) { - if (willPayload && willPayloadLength == 0) { - size_t length = strlen(reinterpret_cast(willPayload)); - if (length > UINT16_MAX) { - emc_log_w("Payload length truncated (l:%zu)", length); - willPayloadLength = UINT16_MAX; - } else { - willPayloadLength = length; - } - } - if (!clientId || strlen(clientId) == 0) { - emc_log_w("clientId not set error"); - error = espMqttClientTypes::Error::MALFORMED_PARAMETER; - return; - } - - // Calculate size - size_t remainingLength = 6 + // protocol - 1 + // protocol level - 1 + // connect flags - 2 + // keepalive - 2 + strlen(clientId) + (willTopic ? 2 + strlen(willTopic) + 2 + willPayloadLength : 0) + (username ? 2 + strlen(username) : 0) - + (password ? 2 + strlen(password) : 0); - - // allocate memory - if (!_allocate(remainingLength, false)) { - error = espMqttClientTypes::Error::OUT_OF_MEMORY; - return; - } - - // serialize - size_t pos = 0; - - // FIXED HEADER - _data[pos++] = PacketType.CONNECT | HeaderFlag.CONNECT_RESERVED; - pos += encodeRemainingLength(remainingLength, &_data[pos]); - pos += encodeString(PROTOCOL, &_data[pos]); - _data[pos++] = PROTOCOL_LEVEL; - uint8_t connectFlags = 0; - if (cleanSession) - connectFlags |= espMqttClientInternals::ConnectFlag.CLEAN_SESSION; - if (username != nullptr) - connectFlags |= espMqttClientInternals::ConnectFlag.USERNAME; - if (password != nullptr) - connectFlags |= espMqttClientInternals::ConnectFlag.PASSWORD; - if (willTopic != nullptr) { - connectFlags |= espMqttClientInternals::ConnectFlag.WILL; - if (willRetain) - connectFlags |= espMqttClientInternals::ConnectFlag.WILL_RETAIN; - switch (willQos) { - case 0: - connectFlags |= espMqttClientInternals::ConnectFlag.WILL_QOS0; - break; - case 1: - connectFlags |= espMqttClientInternals::ConnectFlag.WILL_QOS1; - break; - case 2: - connectFlags |= espMqttClientInternals::ConnectFlag.WILL_QOS2; - break; - } - } - _data[pos++] = connectFlags; - _data[pos++] = keepAlive >> 8; - _data[pos++] = keepAlive & 0xFF; - - // PAYLOAD - // client ID - pos += encodeString(clientId, &_data[pos]); - // will - if (willTopic != nullptr && willPayload != nullptr) { - pos += encodeString(willTopic, &_data[pos]); - _data[pos++] = willPayloadLength >> 8; - _data[pos++] = willPayloadLength & 0xFF; - memcpy(&_data[pos], willPayload, willPayloadLength); - pos += willPayloadLength; - } - // credentials - if (username != nullptr) - pos += encodeString(username, &_data[pos]); - if (password != nullptr) - encodeString(password, &_data[pos]); - - error = espMqttClientTypes::Error::SUCCESS; -} - -Packet::Packet(espMqttClientTypes::Error & error, uint16_t packetId, const char * topic, const uint8_t * payload, size_t payloadLength, uint8_t qos, bool retain) - : _packetId(packetId) - , _data(nullptr) - , _size(0) - , _payloadIndex(0) - , _payloadStartIndex(0) - , _payloadEndIndex(0) - , _getPayload(nullptr) { - size_t remainingLength = 2 + strlen(topic) + // topic length + topic - 2 + // packet ID - payloadLength; - - if (qos == 0) { - remainingLength -= 2; - _packetId = 0; - } - - if (!_allocate(remainingLength)) { - error = espMqttClientTypes::Error::OUT_OF_MEMORY; - return; - } - - size_t pos = _fillPublishHeader(packetId, topic, remainingLength, qos, retain); - - // PAYLOAD - memcpy(&_data[pos], payload, payloadLength); - - error = espMqttClientTypes::Error::SUCCESS; -} - -Packet::Packet(espMqttClientTypes::Error & error, - uint16_t packetId, - const char * topic, - espMqttClientTypes::PayloadCallback payloadCallback, - size_t payloadLength, - uint8_t qos, - bool retain) - : _packetId(packetId) - , _data(nullptr) - , _size(0) - , _payloadIndex(0) - , _payloadStartIndex(0) - , _payloadEndIndex(0) - , _getPayload(payloadCallback) { - size_t remainingLength = 2 + strlen(topic) + // topic length + topic - 2 + // packet ID - payloadLength; - - if (qos == 0) { - remainingLength -= 2; - _packetId = 0; - } - - if (!_allocate(remainingLength - payloadLength + std::min(payloadLength, static_cast(EMC_RX_BUFFER_SIZE)))) { - error = espMqttClientTypes::Error::OUT_OF_MEMORY; - return; - } - - size_t pos = _fillPublishHeader(packetId, topic, remainingLength, qos, retain); - - // payload will be added by 'Packet::available' - _size = pos + payloadLength; - _payloadIndex = pos; - _payloadStartIndex = _payloadIndex; - _payloadEndIndex = _payloadIndex; - - error = espMqttClientTypes::Error::SUCCESS; -} - -Packet::Packet(espMqttClientTypes::Error & error, uint16_t packetId, const char * topic, uint8_t qos) - : _packetId(packetId) - , _data(nullptr) - , _size(0) - , _payloadIndex(0) - , _payloadStartIndex(0) - , _payloadEndIndex(0) - , _getPayload(nullptr) { - SubscribeItem list[1] = {{topic, qos}}; - _createSubscribe(error, list, 1); -} - -Packet::Packet(espMqttClientTypes::Error & error, MQTTPacketType type, uint16_t packetId) - : _packetId(packetId) - , _data(nullptr) - , _size(0) - , _payloadIndex(0) - , _payloadStartIndex(0) - , _payloadEndIndex(0) - , _getPayload(nullptr) { - if (!_allocate(2)) { - error = espMqttClientTypes::Error::OUT_OF_MEMORY; - return; - } - - size_t pos = 0; - _data[pos] = type; - if (type == PacketType.PUBREL) { - _data[pos++] |= HeaderFlag.PUBREL_RESERVED; +Packet::Packet(espMqttClientTypes::Error& error, + bool cleanSession, + const char* username, + const char* password, + const char* willTopic, + bool willRetain, + uint8_t willQos, + const uint8_t* willPayload, + uint16_t willPayloadLength, + uint16_t keepAlive, + const char* clientId) +: _packetId(0) +, _data(nullptr) +, _size(0) +, _payloadIndex(0) +, _payloadStartIndex(0) +, _payloadEndIndex(0) +, _getPayload(nullptr) { + if (willPayload && willPayloadLength == 0) { + size_t length = strlen(reinterpret_cast(willPayload)); + if (length > UINT16_MAX) { + emc_log_w("Payload length truncated (l:%zu)", length); + willPayloadLength = UINT16_MAX; } else { - pos++; + willPayloadLength = length; } - pos += encodeRemainingLength(2, &_data[pos]); - _data[pos++] = packetId >> 8; - _data[pos] = packetId & 0xFF; + } + if (!clientId || strlen(clientId) == 0) { + emc_log_w("clientId not set error"); + error = espMqttClientTypes::Error::MALFORMED_PARAMETER; + return; + } - error = espMqttClientTypes::Error::SUCCESS; + // Calculate size + size_t remainingLength = + 6 + // protocol + 1 + // protocol level + 1 + // connect flags + 2 + // keepalive + 2 + strlen(clientId) + + (willTopic ? 2 + strlen(willTopic) + 2 + willPayloadLength : 0) + + (username ? 2 + strlen(username) : 0) + + (password ? 2 + strlen(password) : 0); + + // allocate memory + if (!_allocate(remainingLength, false)) { + error = espMqttClientTypes::Error::OUT_OF_MEMORY; + return; + } + + // serialize + size_t pos = 0; + + // FIXED HEADER + _data[pos++] = PacketType.CONNECT | HeaderFlag.CONNECT_RESERVED; + pos += encodeRemainingLength(remainingLength, &_data[pos]); + pos += encodeString(PROTOCOL, &_data[pos]); + _data[pos++] = PROTOCOL_LEVEL; + uint8_t connectFlags = 0; + if (cleanSession) connectFlags |= espMqttClientInternals::ConnectFlag.CLEAN_SESSION; + if (username != nullptr) connectFlags |= espMqttClientInternals::ConnectFlag.USERNAME; + if (password != nullptr) connectFlags |= espMqttClientInternals::ConnectFlag.PASSWORD; + if (willTopic != nullptr) { + connectFlags |= espMqttClientInternals::ConnectFlag.WILL; + if (willRetain) connectFlags |= espMqttClientInternals::ConnectFlag.WILL_RETAIN; + switch (willQos) { + case 0: + connectFlags |= espMqttClientInternals::ConnectFlag.WILL_QOS0; + break; + case 1: + connectFlags |= espMqttClientInternals::ConnectFlag.WILL_QOS1; + break; + case 2: + connectFlags |= espMqttClientInternals::ConnectFlag.WILL_QOS2; + break; + } + } + _data[pos++] = connectFlags; + _data[pos++] = keepAlive >> 8; + _data[pos++] = keepAlive & 0xFF; + + // PAYLOAD + // client ID + pos += encodeString(clientId, &_data[pos]); + // will + if (willTopic != nullptr && willPayload != nullptr) { + pos += encodeString(willTopic, &_data[pos]); + _data[pos++] = willPayloadLength >> 8; + _data[pos++] = willPayloadLength & 0xFF; + memcpy(&_data[pos], willPayload, willPayloadLength); + pos += willPayloadLength; + } + // credentials + if (username != nullptr) pos += encodeString(username, &_data[pos]); + if (password != nullptr) encodeString(password, &_data[pos]); + + error = espMqttClientTypes::Error::SUCCESS; } -Packet::Packet(espMqttClientTypes::Error & error, uint16_t packetId, const char * topic) - : _packetId(packetId) - , _data(nullptr) - , _size(0) - , _payloadIndex(0) - , _payloadStartIndex(0) - , _payloadEndIndex(0) - , _getPayload(nullptr) { - const char * list[1] = {topic}; - _createUnsubscribe(error, list, 1); +Packet::Packet(espMqttClientTypes::Error& error, + uint16_t packetId, + const char* topic, + const uint8_t* payload, + size_t payloadLength, + uint8_t qos, + bool retain) +: _packetId(packetId) +, _data(nullptr) +, _size(0) +, _payloadIndex(0) +, _payloadStartIndex(0) +, _payloadEndIndex(0) +, _getPayload(nullptr) { + size_t remainingLength = + 2 + strlen(topic) + // topic length + topic + 2 + // packet ID + payloadLength; + + if (qos == 0) { + remainingLength -= 2; + _packetId = 0; + } + + if (!_allocate(remainingLength, true)) { + error = espMqttClientTypes::Error::OUT_OF_MEMORY; + return; + } + + size_t pos = _fillPublishHeader(packetId, topic, remainingLength, qos, retain); + + // PAYLOAD + memcpy(&_data[pos], payload, payloadLength); + + error = espMqttClientTypes::Error::SUCCESS; } -Packet::Packet(espMqttClientTypes::Error & error, MQTTPacketType type) - : _packetId(0) - , _data(nullptr) - , _size(0) - , _payloadIndex(0) - , _payloadStartIndex(0) - , _payloadEndIndex(0) - , _getPayload(nullptr) { - if (!_allocate(0)) { - error = espMqttClientTypes::Error::OUT_OF_MEMORY; - return; - } - _data[0] |= type; +Packet::Packet(espMqttClientTypes::Error& error, + uint16_t packetId, + const char* topic, + espMqttClientTypes::PayloadCallback payloadCallback, + size_t payloadLength, + uint8_t qos, + bool retain) +: _packetId(packetId) +, _data(nullptr) +, _size(0) +, _payloadIndex(0) +, _payloadStartIndex(0) +, _payloadEndIndex(0) +, _getPayload(payloadCallback) { + size_t remainingLength = + 2 + strlen(topic) + // topic length + topic + 2 + // packet ID + payloadLength; - error = espMqttClientTypes::Error::SUCCESS; + if (qos == 0) { + remainingLength -= 2; + _packetId = 0; + } + + if (!_allocate(remainingLength - payloadLength + std::min(payloadLength, static_cast(EMC_RX_BUFFER_SIZE)), true)) { + error = espMqttClientTypes::Error::OUT_OF_MEMORY; + return; + } + + size_t pos = _fillPublishHeader(packetId, topic, remainingLength, qos, retain); + + // payload will be added by 'Packet::available' + _size = pos + payloadLength; + _payloadIndex = pos; + _payloadStartIndex = _payloadIndex; + _payloadEndIndex = _payloadIndex; + + error = espMqttClientTypes::Error::SUCCESS; +} + +Packet::Packet(espMqttClientTypes::Error& error, uint16_t packetId, const char* topic, uint8_t qos) +: _packetId(packetId) +, _data(nullptr) +, _size(0) +, _payloadIndex(0) +, _payloadStartIndex(0) +, _payloadEndIndex(0) +, _getPayload(nullptr) { + SubscribeItem list[1] = {topic, qos}; + _createSubscribe(error, list, 1); +} + +Packet::Packet(espMqttClientTypes::Error& error, MQTTPacketType type, uint16_t packetId) +: _packetId(packetId) +, _data(nullptr) +, _size(0) +, _payloadIndex(0) +, _payloadStartIndex(0) +, _payloadEndIndex(0) +, _getPayload(nullptr) { + if (!_allocate(2, true)) { + error = espMqttClientTypes::Error::OUT_OF_MEMORY; + return; + } + + size_t pos = 0; + _data[pos] = type; + if (type == PacketType.PUBREL) { + _data[pos++] |= HeaderFlag.PUBREL_RESERVED; + } else { + pos++; + } + pos += encodeRemainingLength(2, &_data[pos]); + _data[pos++] = packetId >> 8; + _data[pos] = packetId & 0xFF; + + error = espMqttClientTypes::Error::SUCCESS; +} + +Packet::Packet(espMqttClientTypes::Error& error, uint16_t packetId, const char* topic) +: _packetId(packetId) +, _data(nullptr) +, _size(0) +, _payloadIndex(0) +, _payloadStartIndex(0) +, _payloadEndIndex(0) +, _getPayload(nullptr) { + const char* list[1] = {topic}; + _createUnsubscribe(error, list, 1); +} + +Packet::Packet(espMqttClientTypes::Error& error, MQTTPacketType type) +: _packetId(0) +, _data(nullptr) +, _size(0) +, _payloadIndex(0) +, _payloadStartIndex(0) +, _payloadEndIndex(0) +, _getPayload(nullptr) { + if (!_allocate(0, true)) { + error = espMqttClientTypes::Error::OUT_OF_MEMORY; + return; + } + _data[0] |= type; + + error = espMqttClientTypes::Error::SUCCESS; } bool Packet::_allocate(size_t remainingLength, bool check) { - if (check && EMC_GET_FREE_MEMORY() < EMC_MIN_FREE_MEMORY) { - emc_log_w("Packet buffer not allocated: low memory"); - return false; - } - _size = 1 + remainingLengthLength(remainingLength) + remainingLength; - _data = reinterpret_cast(malloc(_size)); - if (!_data) { - _size = 0; - emc_log_w("Alloc failed (l:%zu)", _size); - return false; - } - emc_log_i("Alloc (l:%zu)", _size); - memset(_data, 0, _size); - return true; + #if EMC_USE_MEMPOOL + (void) check; + #else + if (check && EMC_GET_FREE_MEMORY() < EMC_MIN_FREE_MEMORY) { + emc_log_w("Packet buffer not allocated: low memory"); + return false; + } + #endif + _size = 1 + remainingLengthLength(remainingLength) + remainingLength; + #if EMC_USE_MEMPOOL + _data = reinterpret_cast(_memPool.malloc(_size)); + #else + _data = reinterpret_cast(malloc(_size)); + #endif + if (!_data) { + _size = 0; + emc_log_w("Alloc failed (l:%zu)", _size); + return false; + } + emc_log_i("Alloc (l:%zu)", _size); + memset(_data, 0, _size); + return true; } -size_t Packet::_fillPublishHeader(uint16_t packetId, const char * topic, size_t remainingLength, uint8_t qos, bool retain) { - size_t index = 0; +size_t Packet::_fillPublishHeader(uint16_t packetId, + const char* topic, + size_t remainingLength, + uint8_t qos, + bool retain) { + size_t index = 0; - // FIXED HEADER - _data[index] = PacketType.PUBLISH; - if (retain) - _data[index] |= HeaderFlag.PUBLISH_RETAIN; - if (qos == 0) { - _data[index++] |= HeaderFlag.PUBLISH_QOS0; - } else if (qos == 1) { - _data[index++] |= HeaderFlag.PUBLISH_QOS1; - } else if (qos == 2) { - _data[index++] |= HeaderFlag.PUBLISH_QOS2; - } - index += encodeRemainingLength(remainingLength, &_data[index]); + // FIXED HEADER + _data[index] = PacketType.PUBLISH; + if (retain) _data[index] |= HeaderFlag.PUBLISH_RETAIN; + if (qos == 0) { + _data[index++] |= HeaderFlag.PUBLISH_QOS0; + } else if (qos == 1) { + _data[index++] |= HeaderFlag.PUBLISH_QOS1; + } else if (qos == 2) { + _data[index++] |= HeaderFlag.PUBLISH_QOS2; + } + index += encodeRemainingLength(remainingLength, &_data[index]); - // VARIABLE HEADER - index += encodeString(topic, &_data[index]); - if (qos > 0) { - _data[index++] = packetId >> 8; - _data[index++] = packetId & 0xFF; - } + // VARIABLE HEADER + index += encodeString(topic, &_data[index]); + if (qos > 0) { + _data[index++] = packetId >> 8; + _data[index++] = packetId & 0xFF; + } - return index; + return index; } -void Packet::_createSubscribe(espMqttClientTypes::Error & error, SubscribeItem * list, size_t numberTopics) { - // Calculate size - size_t payload = 0; - for (size_t i = 0; i < numberTopics; ++i) { - payload += 2 + strlen(list[i].topic) + 1; // length bytes, string, qos - } - size_t remainingLength = 2 + payload; // packetId + payload +void Packet::_createSubscribe(espMqttClientTypes::Error& error, + SubscribeItem* list, + size_t numberTopics) { + // Calculate size + size_t payload = 0; + for (size_t i = 0; i < numberTopics; ++i) { + payload += 2 + strlen(list[i].topic) + 1; // length bytes, string, qos + } + size_t remainingLength = 2 + payload; // packetId + payload - // allocate memory - if (!_allocate(remainingLength)) { - error = espMqttClientTypes::Error::OUT_OF_MEMORY; - return; - } + // allocate memory + if (!_allocate(remainingLength, true)) { + error = espMqttClientTypes::Error::OUT_OF_MEMORY; + return; + } - // serialize - size_t pos = 0; - _data[pos++] = PacketType.SUBSCRIBE | HeaderFlag.SUBSCRIBE_RESERVED; - pos += encodeRemainingLength(remainingLength, &_data[pos]); - _data[pos++] = _packetId >> 8; - _data[pos++] = _packetId & 0xFF; - for (size_t i = 0; i < numberTopics; ++i) { - pos += encodeString(list[i].topic, &_data[pos]); - _data[pos++] = list[i].qos; - } + // serialize + size_t pos = 0; + _data[pos++] = PacketType.SUBSCRIBE | HeaderFlag.SUBSCRIBE_RESERVED; + pos += encodeRemainingLength(remainingLength, &_data[pos]); + _data[pos++] = _packetId >> 8; + _data[pos++] = _packetId & 0xFF; + for (size_t i = 0; i < numberTopics; ++i) { + pos += encodeString(list[i].topic, &_data[pos]); + _data[pos++] = list[i].qos; + } - error = espMqttClientTypes::Error::SUCCESS; + error = espMqttClientTypes::Error::SUCCESS; } -void Packet::_createUnsubscribe(espMqttClientTypes::Error & error, const char ** list, size_t numberTopics) { - // Calculate size - size_t payload = 0; - for (size_t i = 0; i < numberTopics; ++i) { - payload += 2 + strlen(list[i]); // length bytes, string - } - size_t remainingLength = 2 + payload; // packetId + payload +void Packet::_createUnsubscribe(espMqttClientTypes::Error& error, + const char** list, + size_t numberTopics) { + // Calculate size + size_t payload = 0; + for (size_t i = 0; i < numberTopics; ++i) { + payload += 2 + strlen(list[i]); // length bytes, string + } + size_t remainingLength = 2 + payload; // packetId + payload - // allocate memory - if (!_allocate(remainingLength)) { - error = espMqttClientTypes::Error::OUT_OF_MEMORY; - return; - } + // allocate memory + if (!_allocate(remainingLength, true)) { + error = espMqttClientTypes::Error::OUT_OF_MEMORY; + return; + } - // serialize - size_t pos = 0; - _data[pos++] = PacketType.UNSUBSCRIBE | HeaderFlag.UNSUBSCRIBE_RESERVED; - pos += encodeRemainingLength(remainingLength, &_data[pos]); - _data[pos++] = _packetId >> 8; - _data[pos++] = _packetId & 0xFF; - for (size_t i = 0; i < numberTopics; ++i) { - pos += encodeString(list[i], &_data[pos]); - } + // serialize + size_t pos = 0; + _data[pos++] = PacketType.UNSUBSCRIBE | HeaderFlag.UNSUBSCRIBE_RESERVED; + pos += encodeRemainingLength(remainingLength, &_data[pos]); + _data[pos++] = _packetId >> 8; + _data[pos++] = _packetId & 0xFF; + for (size_t i = 0; i < numberTopics; ++i) { + pos += encodeString(list[i], &_data[pos]); + } - error = espMqttClientTypes::Error::SUCCESS; + error = espMqttClientTypes::Error::SUCCESS; } size_t Packet::_chunkedAvailable(size_t index) { - // index vs size check done in 'available(index)' + // index vs size check done in 'available(index)' - // index points to header or first payload byte - if (index < _payloadIndex) { - if (_size > _payloadIndex && _payloadEndIndex != 0) { - size_t copied = _getPayload(&_data[_payloadIndex], std::min(static_cast(EMC_TX_BUFFER_SIZE), _size - _payloadStartIndex), index); - _payloadStartIndex = _payloadIndex; - _payloadEndIndex = _payloadStartIndex + copied - 1; - } - - // index points to payload unavailable - } else if (index > _payloadEndIndex || _payloadStartIndex > index) { - _payloadStartIndex = index; - size_t copied = _getPayload(&_data[_payloadIndex], std::min(static_cast(EMC_TX_BUFFER_SIZE), _size - _payloadStartIndex), index); - _payloadEndIndex = _payloadStartIndex + copied - 1; + // index points to header or first payload byte + if (index < _payloadIndex) { + if (_size > _payloadIndex && _payloadEndIndex != 0) { + size_t copied = _getPayload(&_data[_payloadIndex], std::min(static_cast(EMC_TX_BUFFER_SIZE), _size - _payloadStartIndex), index); + _payloadStartIndex = _payloadIndex; + _payloadEndIndex = _payloadStartIndex + copied - 1; } - // now index points to header or payload available - return _payloadEndIndex - index + 1; + // index points to payload unavailable + } else if (index > _payloadEndIndex || _payloadStartIndex > index) { + _payloadStartIndex = index; + size_t copied = _getPayload(&_data[_payloadIndex], std::min(static_cast(EMC_TX_BUFFER_SIZE), _size - _payloadStartIndex), index); + _payloadEndIndex = _payloadStartIndex + copied - 1; + } + + // now index points to header or payload available + return _payloadEndIndex - index + 1; } -const uint8_t * Packet::_chunkedData(size_t index) const { - // CAUTION!! available(index) has to be called first to check available data and possibly fill payloadbuffer - if (index < _payloadIndex) { - return &_data[index]; - } - return &_data[index - _payloadStartIndex + _payloadIndex]; +const uint8_t* Packet::_chunkedData(size_t index) const { + // CAUTION!! available(index) has to be called first to check available data and possibly fill payloadbuffer + if (index < _payloadIndex) { + return &_data[index]; + } + return &_data[index - _payloadStartIndex + _payloadIndex]; } -} // end namespace espMqttClientInternals +} // end namespace espMqttClientInternals diff --git a/lib/espMqttClient/src/Packets/Packet.h b/lib/espMqttClient/src/Packets/Packet.h index f2b290293..5d0b67b28 100644 --- a/lib/espMqttClient/src/Packets/Packet.h +++ b/lib/espMqttClient/src/Packets/Packet.h @@ -17,7 +17,11 @@ the LICENSE file. #include "../Helpers.h" #include "../Logging.h" #include "RemainingLength.h" -#include "String.h" +#include "StringUtil.h" + +#if EMC_USE_MEMPOOL + #include "MemoryPool/src/MemoryPool.h" +#endif namespace espMqttClientInternals { @@ -133,7 +137,7 @@ class Packet { private: // pass remainingLength = total size - header - remainingLengthLength! - bool _allocate(size_t remainingLength, bool check = true); + bool _allocate(size_t remainingLength, bool check); // fills header and returns index of next available byte in buffer size_t _fillPublishHeader(uint16_t packetId, @@ -150,6 +154,10 @@ class Packet { size_t _chunkedAvailable(size_t index); const uint8_t* _chunkedData(size_t index) const; + + #if EMC_USE_MEMPOOL + static MemoryPool::Variable _memPool; + #endif }; } // end namespace espMqttClientInternals diff --git a/lib/espMqttClient/src/Packets/String.cpp b/lib/espMqttClient/src/Packets/StringUtil.cpp similarity index 96% rename from lib/espMqttClient/src/Packets/String.cpp rename to lib/espMqttClient/src/Packets/StringUtil.cpp index c3fe23fdc..7cd3dd8c4 100644 --- a/lib/espMqttClient/src/Packets/String.cpp +++ b/lib/espMqttClient/src/Packets/StringUtil.cpp @@ -6,7 +6,7 @@ For a copy, see or the LICENSE file. */ -#include "String.h" +#include "StringUtil.h" namespace espMqttClientInternals { diff --git a/lib/espMqttClient/src/Packets/String.h b/lib/espMqttClient/src/Packets/StringUtil.h similarity index 100% rename from lib/espMqttClient/src/Packets/String.h rename to lib/espMqttClient/src/Packets/StringUtil.h diff --git a/lib/espMqttClient/src/Transport/ClientPosix.cpp b/lib/espMqttClient/src/Transport/ClientPosix.cpp index 82f16b449..1cd66e2b9 100644 --- a/lib/espMqttClient/src/Transport/ClientPosix.cpp +++ b/lib/espMqttClient/src/Transport/ClientPosix.cpp @@ -38,9 +38,10 @@ bool ClientPosix::connect(IPAddress ip, uint16_t port) { memset(&_host, 0, sizeof(_host)); _host.sin_family = AF_INET; _host.sin_addr.s_addr = htonl(uint32_t(ip)); - _host.sin_port = htons(port); // modified by proddy for EMS-ESP compiling standalone + _host.sin_port = ::htons(port); int ret = ::connect(_sockfd, reinterpret_cast(&_host), sizeof(_host)); + if (ret < 0) { emc_log_e("Error connecting: %d - (%d) %s", ret, errno, strerror(errno)); return false; diff --git a/lib/espMqttClient/src/Transport/IPAddress.cpp b/lib/espMqttClient/src/Transport/ClientPosixIPAddress.cpp similarity index 94% rename from lib/espMqttClient/src/Transport/IPAddress.cpp rename to lib/espMqttClient/src/Transport/ClientPosixIPAddress.cpp index b198429dc..3386dec85 100644 --- a/lib/espMqttClient/src/Transport/IPAddress.cpp +++ b/lib/espMqttClient/src/Transport/ClientPosixIPAddress.cpp @@ -8,7 +8,7 @@ the LICENSE file. #if defined(__linux__) -#include "IPAddress.h" +#include "ClientPosixIPAddress.h" IPAddress::IPAddress() : _address(0) { diff --git a/lib/espMqttClient/src/Transport/IPAddress.h b/lib/espMqttClient/src/Transport/ClientPosixIPAddress.h similarity index 100% rename from lib/espMqttClient/src/Transport/IPAddress.h rename to lib/espMqttClient/src/Transport/ClientPosixIPAddress.h diff --git a/lib/espMqttClient/src/Transport/ClientSecureSync.h b/lib/espMqttClient/src/Transport/ClientSecureSync.h index f8c47392d..c129296fe 100644 --- a/lib/espMqttClient/src/Transport/ClientSecureSync.h +++ b/lib/espMqttClient/src/Transport/ClientSecureSync.h @@ -17,7 +17,6 @@ the LICENSE file. #else #include #endif - #include "Transport.h" namespace espMqttClientInternals { @@ -32,6 +31,7 @@ class ClientSecureSync : public Transport { void stop() override; bool connected() override; bool disconnected() override; + // added for EMS-ESP #if defined(EMC_CLIENT_SECURE) WiFiClientSecure client; #else diff --git a/lib/espMqttClient/src/Transport/Transport.h b/lib/espMqttClient/src/Transport/Transport.h index 6720c024f..d368d0184 100644 --- a/lib/espMqttClient/src/Transport/Transport.h +++ b/lib/espMqttClient/src/Transport/Transport.h @@ -10,7 +10,7 @@ the LICENSE file. #include // size_t -#include "IPAddress.h" +#include "ClientPosixIPAddress.h" namespace espMqttClientInternals { diff --git a/lib/espMqttClient/src/espMqttClient.cpp b/lib/espMqttClient/src/espMqttClient.cpp index bd9b6935a..bbbfd693e 100644 --- a/lib/espMqttClient/src/espMqttClient.cpp +++ b/lib/espMqttClient/src/espMqttClient.cpp @@ -10,114 +10,114 @@ the LICENSE file. #if defined(ARDUINO_ARCH_ESP8266) espMqttClient::espMqttClient() -: MqttClientSetup(espMqttClientTypes::UseInternalTask::NO) -, _client() { - _transport = &_client; + : MqttClientSetup(espMqttClientTypes::UseInternalTask::NO) + , _client() { + _transport = &_client; } espMqttClientSecure::espMqttClientSecure() -: MqttClientSetup(espMqttClientTypes::UseInternalTask::NO) -, _client() { - _transport = &_client; + : MqttClientSetup(espMqttClientTypes::UseInternalTask::NO) + , _client() { + _transport = &_client; } -espMqttClientSecure& espMqttClientSecure::setInsecure() { - _client.client.setInsecure(); - return *this; +espMqttClientSecure & espMqttClientSecure::setInsecure() { + _client.client.setInsecure(); + return *this; } -espMqttClientSecure& espMqttClientSecure::setFingerprint(const uint8_t fingerprint[20]) { - _client.client.setFingerprint(fingerprint); - return *this; +espMqttClientSecure & espMqttClientSecure::setFingerprint(const uint8_t fingerprint[20]) { + _client.client.setFingerprint(fingerprint); + return *this; } -espMqttClientSecure& espMqttClientSecure::setTrustAnchors(const X509List *ta) { - _client.client.setTrustAnchors(ta); - return *this; +espMqttClientSecure & espMqttClientSecure::setTrustAnchors(const X509List * ta) { + _client.client.setTrustAnchors(ta); + return *this; } -espMqttClientSecure& espMqttClientSecure::setClientRSACert(const X509List *cert, const PrivateKey *sk) { - _client.client.setClientRSACert(cert, sk); - return *this; +espMqttClientSecure & espMqttClientSecure::setClientRSACert(const X509List * cert, const PrivateKey * sk) { + _client.client.setClientRSACert(cert, sk); + return *this; } -espMqttClientSecure& espMqttClientSecure::setClientECCert(const X509List *cert, const PrivateKey *sk, unsigned allowed_usages, unsigned cert_issuer_key_type) { - _client.client.setClientECCert(cert, sk, allowed_usages, cert_issuer_key_type); - return *this; +espMqttClientSecure & espMqttClientSecure::setClientECCert(const X509List * cert, const PrivateKey * sk, unsigned allowed_usages, unsigned cert_issuer_key_type) { + _client.client.setClientECCert(cert, sk, allowed_usages, cert_issuer_key_type); + return *this; } -espMqttClientSecure& espMqttClientSecure::setCertStore(CertStoreBase *certStore) { - _client.client.setCertStore(certStore); - return *this; +espMqttClientSecure & espMqttClientSecure::setCertStore(CertStoreBase * certStore) { + _client.client.setCertStore(certStore); + return *this; } #endif #if defined(ARDUINO_ARCH_ESP32) espMqttClient::espMqttClient(espMqttClientTypes::UseInternalTask useInternalTask) -: MqttClientSetup(useInternalTask) -, _client() { - _transport = &_client; + : MqttClientSetup(useInternalTask) + , _client() { + _transport = &_client; } espMqttClient::espMqttClient(uint8_t priority, uint8_t core) -: MqttClientSetup(espMqttClientTypes::UseInternalTask::YES, priority, core) -, _client() { - _transport = &_client; + : MqttClientSetup(espMqttClientTypes::UseInternalTask::YES, priority, core) + , _client() { + _transport = &_client; } espMqttClientSecure::espMqttClientSecure(espMqttClientTypes::UseInternalTask useInternalTask) -: MqttClientSetup(useInternalTask) -, _client() { - _transport = &_client; + : MqttClientSetup(useInternalTask) + , _client() { + _transport = &_client; } espMqttClientSecure::espMqttClientSecure(uint8_t priority, uint8_t core) -: MqttClientSetup(espMqttClientTypes::UseInternalTask::YES, priority, core) -, _client() { - _transport = &_client; + : MqttClientSetup(espMqttClientTypes::UseInternalTask::YES, priority, core) + , _client() { + _transport = &_client; } -espMqttClientSecure& espMqttClientSecure::setInsecure() { +espMqttClientSecure & espMqttClientSecure::setInsecure() { #if defined(EMC_CLIENT_SECURE) - _client.client.setInsecure(); + _client.client.setInsecure(); #endif - return *this; + return *this; } -espMqttClientSecure& espMqttClientSecure::setCACert(const char* rootCA) { +espMqttClientSecure & espMqttClientSecure::setCACert(const char * rootCA) { #if defined(EMC_CLIENT_SECURE) - _client.client.setCACert(rootCA); + _client.client.setCACert(rootCA); #endif - return *this; + return *this; } -espMqttClientSecure& espMqttClientSecure::setCertificate(const char* clientCa) { +espMqttClientSecure & espMqttClientSecure::setCertificate(const char * clientCa) { #if defined(EMC_CLIENT_SECURE) - _client.client.setCertificate(clientCa); + _client.client.setCertificate(clientCa); #endif - return *this; + return *this; } -espMqttClientSecure& espMqttClientSecure::setPrivateKey(const char* privateKey) { +espMqttClientSecure & espMqttClientSecure::setPrivateKey(const char * privateKey) { #if defined(EMC_CLIENT_SECURE) - _client.client.setPrivateKey(privateKey); + _client.client.setPrivateKey(privateKey); #endif - return *this; + return *this; } -espMqttClientSecure& espMqttClientSecure::setPreSharedKey(const char* pskIdent, const char* psKey) { +espMqttClientSecure & espMqttClientSecure::setPreSharedKey(const char * pskIdent, const char * psKey) { #if defined(EMC_CLIENT_SECURE) - _client.client.setPreSharedKey(pskIdent, psKey); + _client.client.setPreSharedKey(pskIdent, psKey); #endif - return *this; + return *this; } #endif #if defined(__linux__) espMqttClient::espMqttClient() -: MqttClientSetup(espMqttClientTypes::UseInternalTask::NO) -, _client() { - _transport = &_client; + : MqttClientSetup(espMqttClientTypes::UseInternalTask::NO) + , _client() { + _transport = &_client; } #endif diff --git a/lib/uuid-common/src/uuid/common.h b/lib/uuid-common/src/uuid/common.h index ae0baee64..d8c1ef572 100644 --- a/lib/uuid-common/src/uuid/common.h +++ b/lib/uuid-common/src/uuid/common.h @@ -16,7 +16,7 @@ * along with this program. If not, see . */ -// compare_flash_string added by Proddy +// compare_flash_string added by Proddy for EMS-ESP #ifndef UUID_COMMON_H_ #define UUID_COMMON_H_ @@ -102,8 +102,8 @@ void loop(); */ uint64_t get_uptime_ms(); -uint32_t get_uptime(); -uint32_t get_uptime_sec(); +uint32_t get_uptime(); +uint32_t get_uptime_sec(); void set_uptime(); diff --git a/lib/uuid-log/src/uuid/log.h b/lib/uuid-log/src/uuid/log.h index 32496a05b..66e932a23 100644 --- a/lib/uuid-log/src/uuid/log.h +++ b/lib/uuid-log/src/uuid/log.h @@ -64,7 +64,7 @@ namespace uuid { * - Documentation */ -// ANSI Colors - added by Proddy +// ANSI Colors - added by Proddy for EMS-ESP // See https://www.lihaoyi.com/post/BuildyourownCommandLinewithANSIescapecodes.html #define COLOR_RESET "\x1B[0m" #define COLOR_BLACK "\x1B[0;30m" diff --git a/lib_standalone/ESPAsyncWebServer.h b/lib_standalone/ESPAsyncWebServer.h index 504365b59..57e0d7c08 100644 --- a/lib_standalone/ESPAsyncWebServer.h +++ b/lib_standalone/ESPAsyncWebServer.h @@ -92,18 +92,18 @@ class AsyncWebServerRequest { _method = method_s; } - void addInterestingHeader(const String & name){}; + void addInterestingHeader(const String & name) {}; size_t args() const { return 0; } - void send(AsyncWebServerResponse * response){}; - void send(AsyncJsonResponse * response){}; - void send(PrettyAsyncJsonResponse * response){}; - void send(MsgpackAsyncJsonResponse * response){}; - void send(int code, const String & contentType = String(), const String & content = String()){}; - void send(int code, const String & contentType, const __FlashStringHelper *){}; + void send(AsyncWebServerResponse * response) {}; + void send(AsyncJsonResponse * response) {}; + void send(PrettyAsyncJsonResponse * response) {}; + void send(MsgpackAsyncJsonResponse * response) {}; + void send(int code, const String & contentType = String(), const String & content = String()) {}; + void send(int code, const String & contentType, const __FlashStringHelper *) {}; const String & url() const { return _url; @@ -213,7 +213,7 @@ class AsyncWebServerResponse { typedef std::function ArRequestHandlerFunction; typedef std::function ArUploadHandlerFunction; typedef std::function ArBodyHandlerFunction; -typedef std::function ArJsonRequestHandlerFunction; // added by proddy +typedef std::function ArJsonRequestHandlerFunction; // added by proddy for EMS-ESP class AsyncWebServer { protected: @@ -225,15 +225,15 @@ class AsyncWebServer { ~AsyncWebServer(){}; - void begin(){}; + void begin() {}; void end(); AsyncWebHandler & addHandler(AsyncWebHandler * handler) { return *handler; } - void on(const char * uri, WebRequestMethodComposite method, ArRequestHandlerFunction onRequest){}; - void on(const char * uri, ArJsonRequestHandlerFunction onRequest){}; // added by proddy + void on(const char * uri, WebRequestMethodComposite method, ArRequestHandlerFunction onRequest) {}; + void on(const char * uri, ArJsonRequestHandlerFunction onRequest) {}; // added by proddy for EMS-ESP }; @@ -246,7 +246,7 @@ class AsyncEventSource : public AsyncWebHandler { return 1; } - void send(const char * message, const char * event = NULL, uint32_t id = 0, uint32_t reconnect = 0){}; + void send(const char * message, const char * event = NULL, uint32_t id = 0, uint32_t reconnect = 0) {}; }; diff --git a/lib_standalone/Network.h b/lib_standalone/Network.h index 7dad09f10..838e9d71c 100644 --- a/lib_standalone/Network.h +++ b/lib_standalone/Network.h @@ -4,7 +4,7 @@ #include "Arduino.h" #include -#include +#include #define WiFiMode_t wifi_mode_t #define WIFI_OFF WIFI_MODE_NULL @@ -162,7 +162,7 @@ class WiFiClass { return 0; }; - void disconnect(bool v){}; + void disconnect(bool v) {}; char * getHostname() { return nullptr; @@ -183,7 +183,7 @@ class ETHClass { return false; }; - void setHostname(const char * s){}; + void setHostname(const char * s) {}; char * getHostname() { return nullptr; diff --git a/mock-api/rest_server.ts b/mock-api/rest_server.ts index 8ee5a404a..fbc601214 100644 --- a/mock-api/rest_server.ts +++ b/mock-api/rest_server.ts @@ -381,7 +381,11 @@ const system_status = { free_heap: 143, ntp_status: 2, mqtt_status: true, - ap_status: false + ap_status: false, + network_status: 3, // wifi connected + // network_status: 10, // ethernet connected + // network_status: 6, // wifi disconnected + wifi_rssi: -41 }; let security_settings = { @@ -2134,19 +2138,26 @@ let emsesp_modules = { "modules": [ { id: 1, - "name": "ModuleTest1", + "key": "ModuleTest1", + "name": "Module Test 1", "author": "proddy", "version": "1.0.0", "enabled": true, - "status": "active" + "status": 1, + "message": "Ready", + "license": "1234567890" }, { id: 2, - "name": "ModuleTest2", + "key": "ModuleTest2", + "name": "Module Test 2", "author": "proddy", "version": "1.0.0", "enabled": true, - "status": "active" + "status": 2, + "message": "Ready", + "license": "ABCDEFGHIJKL" + } ] } @@ -2386,7 +2397,9 @@ router return status(200); }) .get(VERIFY_AUTHORIZATION_ENDPOINT, () => verify_authentication) - .post(RESTART_ENDPOINT, () => status(200)) + .post(RESTART_ENDPOINT, () => { + console.log('restarting...'); + return status(200);}) .post(FACTORY_RESET_ENDPOINT, () => status(200)) .post(SIGN_IN_ENDPOINT, () => signin) .get(GENERATE_TOKEN_ENDPOINT, () => generate_token); @@ -2514,10 +2527,11 @@ router const content = await request.json(); let modules = content.modules; for (let i = 0; i < modules.length; i++) { - const name = modules[i].name; - const objIndex = emsesp_modules.modules.findIndex((obj: any) => obj.name === name); + const key = modules[i].key; + const objIndex = emsesp_modules.modules.findIndex((obj: any) => obj.key === key); if (objIndex !== -1) { emsesp_modules.modules[objIndex].enabled = modules[i].enabled; + emsesp_modules.modules[objIndex].license = modules[i].license; } } console.log('modules updated', emsesp_modules); diff --git a/src/web/WebModulesService.cpp b/src/web/WebModulesService.cpp index c9eaa73f1..3f00b6330 100644 --- a/src/web/WebModulesService.cpp +++ b/src/web/WebModulesService.cpp @@ -32,55 +32,62 @@ WebModulesService::WebModulesService(AsyncWebServer * server, FS * fs, SecurityM // load the settings when the service starts void WebModulesService::begin() { EMSESP::logger().info("Starting Modules service"); - moduleLibrary.start(emsesp_); - _fsPersistence.readFromFS(); // read the file from FS +#ifdef EMSESP_TEST + moduleLibrary.start(emsesp_, true); // hot load the test modules as well +#else + moduleLibrary.start(emsesp_); +#endif + + _fsPersistence.readFromFS(); // read the file from FS - this will call the update function WebModules::update() } void WebModulesService::loop() { moduleLibrary.loop(); // loop the external library modules } -// this creates the modules file, saving it to the FS -// and also calls when the Modules web page is refreshed +// this creates the modules settings file, saving it to the file system +// it adds data to an empty 'root' json object +// and also calls when the Modules web page is refreshed/loaded void WebModules::read(WebModules & webModules, JsonObject root) { -#ifdef EMSESP_DEBUG - EMSESP::logger().debug("module read called"); -#endif - + emsesp_->logger().err("debug: in WebModules::read()"); // TODO remove JsonDocument doc_modules; JsonObject root_modules = doc_modules.to(); - moduleLibrary.list(root_modules); // get list the external library modules, put in root json object + moduleLibrary.list(root_modules); // get list the external library modules, put in a json object JsonArray modules = root["modules"].to(); uint8_t counter = 0; for (const JsonObject module : root_modules["modules"].as()) { JsonObject mi = modules.add(); mi["id"] = counter++; // id is only used to render the table and must be unique + mi["key"] = module["key"].as(); mi["name"] = module["name"].as(); mi["author"] = module["author"].as(); mi["version"] = module["version"].as(); - mi["status"] = module["status"].as(); + mi["status"] = module["status"].as(); mi["enabled"] = module["enabled"].as(); + mi["message"] = module["message"].as(); + mi["license"] = module["license"].as(); } } -// read any Module settings from the FS settings -// and then apply the enable/disable -// it's also called on a save +// read any Module settings from the settings file +// and then apply the enable/disable that is set by the user +// This function is called when ems-esp boots and also on a save from the Modules web page StateUpdateResult WebModules::update(JsonObject root, WebModules & webModules) { -#ifdef EMSESP_DEBUG - EMSESP::logger().debug("module update called"); -#endif - if (root["modules"].is()) { for (const JsonObject module : root["modules"].as()) { - // set enabled/disabled - moduleLibrary.enable(module["name"], module["enabled"].as()); + auto key = module["key"].as(); + auto license = module["license"].as(); + auto enable = module["enabled"].as(); + + if (!moduleLibrary.enable(key, license, enable)) { + return StateUpdateResult::ERROR; + } } } - return StateUpdateResult::CHANGED_RESTART; + return StateUpdateResult::CHANGED; } } // namespace emsesp diff --git a/src/web/WebSettingsService.cpp b/src/web/WebSettingsService.cpp index 68be0b171..e3805d037 100644 --- a/src/web/WebSettingsService.cpp +++ b/src/web/WebSettingsService.cpp @@ -251,7 +251,6 @@ StateUpdateResult WebSettings::update(JsonObject root, WebSettings & settings) { settings.analog_enabled = root["analog_enabled"] | EMSESP_DEFAULT_ANALOG_ENABLED; check_flag(prev, settings.analog_enabled, ChangeFlags::ADC); - // // these need system restarts first before settings are activated... // @@ -294,7 +293,6 @@ StateUpdateResult WebSettings::update(JsonObject root, WebSettings & settings) { // // without checks or necessary restarts... // - settings.trace_raw = root["trace_raw"] | EMSESP_DEFAULT_TRACELOG_RAW; EMSESP::trace_raw(settings.trace_raw); diff --git a/src/web/WebStatusService.cpp b/src/web/WebStatusService.cpp index 3d988176a..105931f5e 100644 --- a/src/web/WebStatusService.cpp +++ b/src/web/WebStatusService.cpp @@ -70,6 +70,14 @@ void WebStatusService::systemStatus(AsyncWebServerRequest * request) { root["ap_status"] = EMSESP::esp8266React.apStatus(); + if (emsesp::EMSESP::system_.ethernet_connected()) { + root["network_status"] = 10; // custom code #10 - ETHERNET_STATUS_CONNECTED + root["wifi_rssi"] = 0; + } else { + root["network_status"] = static_cast(WiFi.status()); + root["wifi_rssi"] = WiFi.RSSI(); + } + response->setLength(); request->send(response); }