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/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/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/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/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); }