Mirror of espurna firmware for wireless switches and more
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

703 lines
20 KiB

7 years ago
7 years ago
Terminal: change command-line parser (#2247) Change the underlying command line handling: - switch to a custom parser, inspired by redis / sds - update terminalRegisterCommand signature, pass only bare minimum - clean-up `help` & `commands`. update settings `set`, `get` and `del` - allow our custom test suite to run command-line tests - clean-up Stream IO to allow us to print large things into debug stream (for example, `eeprom.dump`) - send parsing errors to the debug log As a proof of concept, introduce `TERMINAL_MQTT_SUPPORT` and `TERMINAL_WEB_API_SUPPORT` - MQTT subscribes to the `<root>/cmd/set` and sends response to the `<root>/cmd`. We can't output too much, as we don't have any large-send API. - Web API listens to the `/api/cmd?apikey=...&line=...` (or PUT, params inside the body). This one is intended as a possible replacement of the `API_SUPPORT`. Internals introduce a 'task' around the AsyncWebServerRequest object that will simulate what WiFiClient does and push data into it continuously, switching between CONT and SYS. Both are experimental. We only accept a single command and not every command is updated to use Print `ctx.output` object. We are also somewhat limited by the Print / Stream overall, perhaps I am overestimating the usefulness of Arduino compatibility to such an extent :) Web API handler can also sometimes show only part of the result, whenever the command tries to yield() by itself waiting for something. Perhaps we would need to create a custom request handler for that specific use-case.
4 years ago
5 years ago
Terminal: change command-line parser (#2247) Change the underlying command line handling: - switch to a custom parser, inspired by redis / sds - update terminalRegisterCommand signature, pass only bare minimum - clean-up `help` & `commands`. update settings `set`, `get` and `del` - allow our custom test suite to run command-line tests - clean-up Stream IO to allow us to print large things into debug stream (for example, `eeprom.dump`) - send parsing errors to the debug log As a proof of concept, introduce `TERMINAL_MQTT_SUPPORT` and `TERMINAL_WEB_API_SUPPORT` - MQTT subscribes to the `<root>/cmd/set` and sends response to the `<root>/cmd`. We can't output too much, as we don't have any large-send API. - Web API listens to the `/api/cmd?apikey=...&line=...` (or PUT, params inside the body). This one is intended as a possible replacement of the `API_SUPPORT`. Internals introduce a 'task' around the AsyncWebServerRequest object that will simulate what WiFiClient does and push data into it continuously, switching between CONT and SYS. Both are experimental. We only accept a single command and not every command is updated to use Print `ctx.output` object. We are also somewhat limited by the Print / Stream overall, perhaps I am overestimating the usefulness of Arduino compatibility to such an extent :) Web API handler can also sometimes show only part of the result, whenever the command tries to yield() by itself waiting for something. Perhaps we would need to create a custom request handler for that specific use-case.
4 years ago
Terminal: change command-line parser (#2247) Change the underlying command line handling: - switch to a custom parser, inspired by redis / sds - update terminalRegisterCommand signature, pass only bare minimum - clean-up `help` & `commands`. update settings `set`, `get` and `del` - allow our custom test suite to run command-line tests - clean-up Stream IO to allow us to print large things into debug stream (for example, `eeprom.dump`) - send parsing errors to the debug log As a proof of concept, introduce `TERMINAL_MQTT_SUPPORT` and `TERMINAL_WEB_API_SUPPORT` - MQTT subscribes to the `<root>/cmd/set` and sends response to the `<root>/cmd`. We can't output too much, as we don't have any large-send API. - Web API listens to the `/api/cmd?apikey=...&line=...` (or PUT, params inside the body). This one is intended as a possible replacement of the `API_SUPPORT`. Internals introduce a 'task' around the AsyncWebServerRequest object that will simulate what WiFiClient does and push data into it continuously, switching between CONT and SYS. Both are experimental. We only accept a single command and not every command is updated to use Print `ctx.output` object. We are also somewhat limited by the Print / Stream overall, perhaps I am overestimating the usefulness of Arduino compatibility to such an extent :) Web API handler can also sometimes show only part of the result, whenever the command tries to yield() by itself waiting for something. Perhaps we would need to create a custom request handler for that specific use-case.
4 years ago
Terminal: change command-line parser (#2247) Change the underlying command line handling: - switch to a custom parser, inspired by redis / sds - update terminalRegisterCommand signature, pass only bare minimum - clean-up `help` & `commands`. update settings `set`, `get` and `del` - allow our custom test suite to run command-line tests - clean-up Stream IO to allow us to print large things into debug stream (for example, `eeprom.dump`) - send parsing errors to the debug log As a proof of concept, introduce `TERMINAL_MQTT_SUPPORT` and `TERMINAL_WEB_API_SUPPORT` - MQTT subscribes to the `<root>/cmd/set` and sends response to the `<root>/cmd`. We can't output too much, as we don't have any large-send API. - Web API listens to the `/api/cmd?apikey=...&line=...` (or PUT, params inside the body). This one is intended as a possible replacement of the `API_SUPPORT`. Internals introduce a 'task' around the AsyncWebServerRequest object that will simulate what WiFiClient does and push data into it continuously, switching between CONT and SYS. Both are experimental. We only accept a single command and not every command is updated to use Print `ctx.output` object. We are also somewhat limited by the Print / Stream overall, perhaps I am overestimating the usefulness of Arduino compatibility to such an extent :) Web API handler can also sometimes show only part of the result, whenever the command tries to yield() by itself waiting for something. Perhaps we would need to create a custom request handler for that specific use-case.
4 years ago
Terminal: change command-line parser (#2247) Change the underlying command line handling: - switch to a custom parser, inspired by redis / sds - update terminalRegisterCommand signature, pass only bare minimum - clean-up `help` & `commands`. update settings `set`, `get` and `del` - allow our custom test suite to run command-line tests - clean-up Stream IO to allow us to print large things into debug stream (for example, `eeprom.dump`) - send parsing errors to the debug log As a proof of concept, introduce `TERMINAL_MQTT_SUPPORT` and `TERMINAL_WEB_API_SUPPORT` - MQTT subscribes to the `<root>/cmd/set` and sends response to the `<root>/cmd`. We can't output too much, as we don't have any large-send API. - Web API listens to the `/api/cmd?apikey=...&line=...` (or PUT, params inside the body). This one is intended as a possible replacement of the `API_SUPPORT`. Internals introduce a 'task' around the AsyncWebServerRequest object that will simulate what WiFiClient does and push data into it continuously, switching between CONT and SYS. Both are experimental. We only accept a single command and not every command is updated to use Print `ctx.output` object. We are also somewhat limited by the Print / Stream overall, perhaps I am overestimating the usefulness of Arduino compatibility to such an extent :) Web API handler can also sometimes show only part of the result, whenever the command tries to yield() by itself waiting for something. Perhaps we would need to create a custom request handler for that specific use-case.
4 years ago
Terminal: change command-line parser (#2247) Change the underlying command line handling: - switch to a custom parser, inspired by redis / sds - update terminalRegisterCommand signature, pass only bare minimum - clean-up `help` & `commands`. update settings `set`, `get` and `del` - allow our custom test suite to run command-line tests - clean-up Stream IO to allow us to print large things into debug stream (for example, `eeprom.dump`) - send parsing errors to the debug log As a proof of concept, introduce `TERMINAL_MQTT_SUPPORT` and `TERMINAL_WEB_API_SUPPORT` - MQTT subscribes to the `<root>/cmd/set` and sends response to the `<root>/cmd`. We can't output too much, as we don't have any large-send API. - Web API listens to the `/api/cmd?apikey=...&line=...` (or PUT, params inside the body). This one is intended as a possible replacement of the `API_SUPPORT`. Internals introduce a 'task' around the AsyncWebServerRequest object that will simulate what WiFiClient does and push data into it continuously, switching between CONT and SYS. Both are experimental. We only accept a single command and not every command is updated to use Print `ctx.output` object. We are also somewhat limited by the Print / Stream overall, perhaps I am overestimating the usefulness of Arduino compatibility to such an extent :) Web API handler can also sometimes show only part of the result, whenever the command tries to yield() by itself waiting for something. Perhaps we would need to create a custom request handler for that specific use-case.
4 years ago
Terminal: change command-line parser (#2247) Change the underlying command line handling: - switch to a custom parser, inspired by redis / sds - update terminalRegisterCommand signature, pass only bare minimum - clean-up `help` & `commands`. update settings `set`, `get` and `del` - allow our custom test suite to run command-line tests - clean-up Stream IO to allow us to print large things into debug stream (for example, `eeprom.dump`) - send parsing errors to the debug log As a proof of concept, introduce `TERMINAL_MQTT_SUPPORT` and `TERMINAL_WEB_API_SUPPORT` - MQTT subscribes to the `<root>/cmd/set` and sends response to the `<root>/cmd`. We can't output too much, as we don't have any large-send API. - Web API listens to the `/api/cmd?apikey=...&line=...` (or PUT, params inside the body). This one is intended as a possible replacement of the `API_SUPPORT`. Internals introduce a 'task' around the AsyncWebServerRequest object that will simulate what WiFiClient does and push data into it continuously, switching between CONT and SYS. Both are experimental. We only accept a single command and not every command is updated to use Print `ctx.output` object. We are also somewhat limited by the Print / Stream overall, perhaps I am overestimating the usefulness of Arduino compatibility to such an extent :) Web API handler can also sometimes show only part of the result, whenever the command tries to yield() by itself waiting for something. Perhaps we would need to create a custom request handler for that specific use-case.
4 years ago
Terminal: change command-line parser (#2247) Change the underlying command line handling: - switch to a custom parser, inspired by redis / sds - update terminalRegisterCommand signature, pass only bare minimum - clean-up `help` & `commands`. update settings `set`, `get` and `del` - allow our custom test suite to run command-line tests - clean-up Stream IO to allow us to print large things into debug stream (for example, `eeprom.dump`) - send parsing errors to the debug log As a proof of concept, introduce `TERMINAL_MQTT_SUPPORT` and `TERMINAL_WEB_API_SUPPORT` - MQTT subscribes to the `<root>/cmd/set` and sends response to the `<root>/cmd`. We can't output too much, as we don't have any large-send API. - Web API listens to the `/api/cmd?apikey=...&line=...` (or PUT, params inside the body). This one is intended as a possible replacement of the `API_SUPPORT`. Internals introduce a 'task' around the AsyncWebServerRequest object that will simulate what WiFiClient does and push data into it continuously, switching between CONT and SYS. Both are experimental. We only accept a single command and not every command is updated to use Print `ctx.output` object. We are also somewhat limited by the Print / Stream overall, perhaps I am overestimating the usefulness of Arduino compatibility to such an extent :) Web API handler can also sometimes show only part of the result, whenever the command tries to yield() by itself waiting for something. Perhaps we would need to create a custom request handler for that specific use-case.
4 years ago
Terminal: change command-line parser (#2247) Change the underlying command line handling: - switch to a custom parser, inspired by redis / sds - update terminalRegisterCommand signature, pass only bare minimum - clean-up `help` & `commands`. update settings `set`, `get` and `del` - allow our custom test suite to run command-line tests - clean-up Stream IO to allow us to print large things into debug stream (for example, `eeprom.dump`) - send parsing errors to the debug log As a proof of concept, introduce `TERMINAL_MQTT_SUPPORT` and `TERMINAL_WEB_API_SUPPORT` - MQTT subscribes to the `<root>/cmd/set` and sends response to the `<root>/cmd`. We can't output too much, as we don't have any large-send API. - Web API listens to the `/api/cmd?apikey=...&line=...` (or PUT, params inside the body). This one is intended as a possible replacement of the `API_SUPPORT`. Internals introduce a 'task' around the AsyncWebServerRequest object that will simulate what WiFiClient does and push data into it continuously, switching between CONT and SYS. Both are experimental. We only accept a single command and not every command is updated to use Print `ctx.output` object. We are also somewhat limited by the Print / Stream overall, perhaps I am overestimating the usefulness of Arduino compatibility to such an extent :) Web API handler can also sometimes show only part of the result, whenever the command tries to yield() by itself waiting for something. Perhaps we would need to create a custom request handler for that specific use-case.
4 years ago
Terminal: change command-line parser (#2247) Change the underlying command line handling: - switch to a custom parser, inspired by redis / sds - update terminalRegisterCommand signature, pass only bare minimum - clean-up `help` & `commands`. update settings `set`, `get` and `del` - allow our custom test suite to run command-line tests - clean-up Stream IO to allow us to print large things into debug stream (for example, `eeprom.dump`) - send parsing errors to the debug log As a proof of concept, introduce `TERMINAL_MQTT_SUPPORT` and `TERMINAL_WEB_API_SUPPORT` - MQTT subscribes to the `<root>/cmd/set` and sends response to the `<root>/cmd`. We can't output too much, as we don't have any large-send API. - Web API listens to the `/api/cmd?apikey=...&line=...` (or PUT, params inside the body). This one is intended as a possible replacement of the `API_SUPPORT`. Internals introduce a 'task' around the AsyncWebServerRequest object that will simulate what WiFiClient does and push data into it continuously, switching between CONT and SYS. Both are experimental. We only accept a single command and not every command is updated to use Print `ctx.output` object. We are also somewhat limited by the Print / Stream overall, perhaps I am overestimating the usefulness of Arduino compatibility to such an extent :) Web API handler can also sometimes show only part of the result, whenever the command tries to yield() by itself waiting for something. Perhaps we would need to create a custom request handler for that specific use-case.
4 years ago
Terminal: change command-line parser (#2247) Change the underlying command line handling: - switch to a custom parser, inspired by redis / sds - update terminalRegisterCommand signature, pass only bare minimum - clean-up `help` & `commands`. update settings `set`, `get` and `del` - allow our custom test suite to run command-line tests - clean-up Stream IO to allow us to print large things into debug stream (for example, `eeprom.dump`) - send parsing errors to the debug log As a proof of concept, introduce `TERMINAL_MQTT_SUPPORT` and `TERMINAL_WEB_API_SUPPORT` - MQTT subscribes to the `<root>/cmd/set` and sends response to the `<root>/cmd`. We can't output too much, as we don't have any large-send API. - Web API listens to the `/api/cmd?apikey=...&line=...` (or PUT, params inside the body). This one is intended as a possible replacement of the `API_SUPPORT`. Internals introduce a 'task' around the AsyncWebServerRequest object that will simulate what WiFiClient does and push data into it continuously, switching between CONT and SYS. Both are experimental. We only accept a single command and not every command is updated to use Print `ctx.output` object. We are also somewhat limited by the Print / Stream overall, perhaps I am overestimating the usefulness of Arduino compatibility to such an extent :) Web API handler can also sometimes show only part of the result, whenever the command tries to yield() by itself waiting for something. Perhaps we would need to create a custom request handler for that specific use-case.
4 years ago
Terminal: change command-line parser (#2247) Change the underlying command line handling: - switch to a custom parser, inspired by redis / sds - update terminalRegisterCommand signature, pass only bare minimum - clean-up `help` & `commands`. update settings `set`, `get` and `del` - allow our custom test suite to run command-line tests - clean-up Stream IO to allow us to print large things into debug stream (for example, `eeprom.dump`) - send parsing errors to the debug log As a proof of concept, introduce `TERMINAL_MQTT_SUPPORT` and `TERMINAL_WEB_API_SUPPORT` - MQTT subscribes to the `<root>/cmd/set` and sends response to the `<root>/cmd`. We can't output too much, as we don't have any large-send API. - Web API listens to the `/api/cmd?apikey=...&line=...` (or PUT, params inside the body). This one is intended as a possible replacement of the `API_SUPPORT`. Internals introduce a 'task' around the AsyncWebServerRequest object that will simulate what WiFiClient does and push data into it continuously, switching between CONT and SYS. Both are experimental. We only accept a single command and not every command is updated to use Print `ctx.output` object. We are also somewhat limited by the Print / Stream overall, perhaps I am overestimating the usefulness of Arduino compatibility to such an extent :) Web API handler can also sometimes show only part of the result, whenever the command tries to yield() by itself waiting for something. Perhaps we would need to create a custom request handler for that specific use-case.
4 years ago
Terminal: change command-line parser (#2247) Change the underlying command line handling: - switch to a custom parser, inspired by redis / sds - update terminalRegisterCommand signature, pass only bare minimum - clean-up `help` & `commands`. update settings `set`, `get` and `del` - allow our custom test suite to run command-line tests - clean-up Stream IO to allow us to print large things into debug stream (for example, `eeprom.dump`) - send parsing errors to the debug log As a proof of concept, introduce `TERMINAL_MQTT_SUPPORT` and `TERMINAL_WEB_API_SUPPORT` - MQTT subscribes to the `<root>/cmd/set` and sends response to the `<root>/cmd`. We can't output too much, as we don't have any large-send API. - Web API listens to the `/api/cmd?apikey=...&line=...` (or PUT, params inside the body). This one is intended as a possible replacement of the `API_SUPPORT`. Internals introduce a 'task' around the AsyncWebServerRequest object that will simulate what WiFiClient does and push data into it continuously, switching between CONT and SYS. Both are experimental. We only accept a single command and not every command is updated to use Print `ctx.output` object. We are also somewhat limited by the Print / Stream overall, perhaps I am overestimating the usefulness of Arduino compatibility to such an extent :) Web API handler can also sometimes show only part of the result, whenever the command tries to yield() by itself waiting for something. Perhaps we would need to create a custom request handler for that specific use-case.
4 years ago
Terminal: change command-line parser (#2247) Change the underlying command line handling: - switch to a custom parser, inspired by redis / sds - update terminalRegisterCommand signature, pass only bare minimum - clean-up `help` & `commands`. update settings `set`, `get` and `del` - allow our custom test suite to run command-line tests - clean-up Stream IO to allow us to print large things into debug stream (for example, `eeprom.dump`) - send parsing errors to the debug log As a proof of concept, introduce `TERMINAL_MQTT_SUPPORT` and `TERMINAL_WEB_API_SUPPORT` - MQTT subscribes to the `<root>/cmd/set` and sends response to the `<root>/cmd`. We can't output too much, as we don't have any large-send API. - Web API listens to the `/api/cmd?apikey=...&line=...` (or PUT, params inside the body). This one is intended as a possible replacement of the `API_SUPPORT`. Internals introduce a 'task' around the AsyncWebServerRequest object that will simulate what WiFiClient does and push data into it continuously, switching between CONT and SYS. Both are experimental. We only accept a single command and not every command is updated to use Print `ctx.output` object. We are also somewhat limited by the Print / Stream overall, perhaps I am overestimating the usefulness of Arduino compatibility to such an extent :) Web API handler can also sometimes show only part of the result, whenever the command tries to yield() by itself waiting for something. Perhaps we would need to create a custom request handler for that specific use-case.
4 years ago
Terminal: change command-line parser (#2247) Change the underlying command line handling: - switch to a custom parser, inspired by redis / sds - update terminalRegisterCommand signature, pass only bare minimum - clean-up `help` & `commands`. update settings `set`, `get` and `del` - allow our custom test suite to run command-line tests - clean-up Stream IO to allow us to print large things into debug stream (for example, `eeprom.dump`) - send parsing errors to the debug log As a proof of concept, introduce `TERMINAL_MQTT_SUPPORT` and `TERMINAL_WEB_API_SUPPORT` - MQTT subscribes to the `<root>/cmd/set` and sends response to the `<root>/cmd`. We can't output too much, as we don't have any large-send API. - Web API listens to the `/api/cmd?apikey=...&line=...` (or PUT, params inside the body). This one is intended as a possible replacement of the `API_SUPPORT`. Internals introduce a 'task' around the AsyncWebServerRequest object that will simulate what WiFiClient does and push data into it continuously, switching between CONT and SYS. Both are experimental. We only accept a single command and not every command is updated to use Print `ctx.output` object. We are also somewhat limited by the Print / Stream overall, perhaps I am overestimating the usefulness of Arduino compatibility to such an extent :) Web API handler can also sometimes show only part of the result, whenever the command tries to yield() by itself waiting for something. Perhaps we would need to create a custom request handler for that specific use-case.
4 years ago
Terminal: change command-line parser (#2247) Change the underlying command line handling: - switch to a custom parser, inspired by redis / sds - update terminalRegisterCommand signature, pass only bare minimum - clean-up `help` & `commands`. update settings `set`, `get` and `del` - allow our custom test suite to run command-line tests - clean-up Stream IO to allow us to print large things into debug stream (for example, `eeprom.dump`) - send parsing errors to the debug log As a proof of concept, introduce `TERMINAL_MQTT_SUPPORT` and `TERMINAL_WEB_API_SUPPORT` - MQTT subscribes to the `<root>/cmd/set` and sends response to the `<root>/cmd`. We can't output too much, as we don't have any large-send API. - Web API listens to the `/api/cmd?apikey=...&line=...` (or PUT, params inside the body). This one is intended as a possible replacement of the `API_SUPPORT`. Internals introduce a 'task' around the AsyncWebServerRequest object that will simulate what WiFiClient does and push data into it continuously, switching between CONT and SYS. Both are experimental. We only accept a single command and not every command is updated to use Print `ctx.output` object. We are also somewhat limited by the Print / Stream overall, perhaps I am overestimating the usefulness of Arduino compatibility to such an extent :) Web API handler can also sometimes show only part of the result, whenever the command tries to yield() by itself waiting for something. Perhaps we would need to create a custom request handler for that specific use-case.
4 years ago
Terminal: change command-line parser (#2247) Change the underlying command line handling: - switch to a custom parser, inspired by redis / sds - update terminalRegisterCommand signature, pass only bare minimum - clean-up `help` & `commands`. update settings `set`, `get` and `del` - allow our custom test suite to run command-line tests - clean-up Stream IO to allow us to print large things into debug stream (for example, `eeprom.dump`) - send parsing errors to the debug log As a proof of concept, introduce `TERMINAL_MQTT_SUPPORT` and `TERMINAL_WEB_API_SUPPORT` - MQTT subscribes to the `<root>/cmd/set` and sends response to the `<root>/cmd`. We can't output too much, as we don't have any large-send API. - Web API listens to the `/api/cmd?apikey=...&line=...` (or PUT, params inside the body). This one is intended as a possible replacement of the `API_SUPPORT`. Internals introduce a 'task' around the AsyncWebServerRequest object that will simulate what WiFiClient does and push data into it continuously, switching between CONT and SYS. Both are experimental. We only accept a single command and not every command is updated to use Print `ctx.output` object. We are also somewhat limited by the Print / Stream overall, perhaps I am overestimating the usefulness of Arduino compatibility to such an extent :) Web API handler can also sometimes show only part of the result, whenever the command tries to yield() by itself waiting for something. Perhaps we would need to create a custom request handler for that specific use-case.
4 years ago
Terminal: change command-line parser (#2247) Change the underlying command line handling: - switch to a custom parser, inspired by redis / sds - update terminalRegisterCommand signature, pass only bare minimum - clean-up `help` & `commands`. update settings `set`, `get` and `del` - allow our custom test suite to run command-line tests - clean-up Stream IO to allow us to print large things into debug stream (for example, `eeprom.dump`) - send parsing errors to the debug log As a proof of concept, introduce `TERMINAL_MQTT_SUPPORT` and `TERMINAL_WEB_API_SUPPORT` - MQTT subscribes to the `<root>/cmd/set` and sends response to the `<root>/cmd`. We can't output too much, as we don't have any large-send API. - Web API listens to the `/api/cmd?apikey=...&line=...` (or PUT, params inside the body). This one is intended as a possible replacement of the `API_SUPPORT`. Internals introduce a 'task' around the AsyncWebServerRequest object that will simulate what WiFiClient does and push data into it continuously, switching between CONT and SYS. Both are experimental. We only accept a single command and not every command is updated to use Print `ctx.output` object. We are also somewhat limited by the Print / Stream overall, perhaps I am overestimating the usefulness of Arduino compatibility to such an extent :) Web API handler can also sometimes show only part of the result, whenever the command tries to yield() by itself waiting for something. Perhaps we would need to create a custom request handler for that specific use-case.
4 years ago
7 years ago
6 years ago
6 years ago
6 years ago
api: rework plain and JSON implementations (#2405) - match paths through a custom AsyncWebHandler instead of using generic not-found fallback handler - allow MQTT-like patterns when registering paths (`simple/path`, `path/+/something`, `path/#`) Replaces `relay/0`, `relay/1` etc. with `relay/+`. Magnitudes are plain paths, but using `/+` in case there's more than 1 magnitude of the same type. - restore `std::function` as callback container (no more single-byte arg nonsense). Still, limit to 1 type per handler type - adds JSON handlers which will receive JsonObject root as both input and output. Same logic as plain - GET returns resource data, PUT updates it. - breaking change to `apiAuthenticate(request)`, it no longer will do `request->send(403)` and expect this to be handled externally. - allow `Api-Key` header containing the key, works for both GET & PUT plain requests. The only way to set apikey for JSON. - add `ApiRequest::param` to retrieve both GET and PUT params (aka args), remove ApiBuffer - remove `API_BUFFER_SIZE`. Allow custom form-data key=value pairs for requests, allow to send basic `String`. - add `API_JSON_BUFFER_SIZE` for the JSON buffer (both input and output) - `/apis` replaced with `/api/list`, no longer uses custom handler and is an `apiRegister` callback - `/api/rpc` custom handler replaced with an `apiRegister` callback WIP further down: - no more `webLog` for API requests, unless `webAccessLog` / `WEB_ACCESS_LOG` is set to `1`. This also needs to happen to the other handlers. - migrate to ArduinoJson v6, since it become apparent it is actually a good upgrade :) - actually make use of JSON endpoints more, right now it's just existing GET for sensors and relays - fork ESPAsyncWebServer to cleanup path parsing and temporary objects attached to the request (also, fix things a lot of things based on PRs there...)
3 years ago
6 years ago
6 years ago
api: rework plain and JSON implementations (#2405) - match paths through a custom AsyncWebHandler instead of using generic not-found fallback handler - allow MQTT-like patterns when registering paths (`simple/path`, `path/+/something`, `path/#`) Replaces `relay/0`, `relay/1` etc. with `relay/+`. Magnitudes are plain paths, but using `/+` in case there's more than 1 magnitude of the same type. - restore `std::function` as callback container (no more single-byte arg nonsense). Still, limit to 1 type per handler type - adds JSON handlers which will receive JsonObject root as both input and output. Same logic as plain - GET returns resource data, PUT updates it. - breaking change to `apiAuthenticate(request)`, it no longer will do `request->send(403)` and expect this to be handled externally. - allow `Api-Key` header containing the key, works for both GET & PUT plain requests. The only way to set apikey for JSON. - add `ApiRequest::param` to retrieve both GET and PUT params (aka args), remove ApiBuffer - remove `API_BUFFER_SIZE`. Allow custom form-data key=value pairs for requests, allow to send basic `String`. - add `API_JSON_BUFFER_SIZE` for the JSON buffer (both input and output) - `/apis` replaced with `/api/list`, no longer uses custom handler and is an `apiRegister` callback - `/api/rpc` custom handler replaced with an `apiRegister` callback WIP further down: - no more `webLog` for API requests, unless `webAccessLog` / `WEB_ACCESS_LOG` is set to `1`. This also needs to happen to the other handlers. - migrate to ArduinoJson v6, since it become apparent it is actually a good upgrade :) - actually make use of JSON endpoints more, right now it's just existing GET for sensors and relays - fork ESPAsyncWebServer to cleanup path parsing and temporary objects attached to the request (also, fix things a lot of things based on PRs there...)
3 years ago
api: rework plain and JSON implementations (#2405) - match paths through a custom AsyncWebHandler instead of using generic not-found fallback handler - allow MQTT-like patterns when registering paths (`simple/path`, `path/+/something`, `path/#`) Replaces `relay/0`, `relay/1` etc. with `relay/+`. Magnitudes are plain paths, but using `/+` in case there's more than 1 magnitude of the same type. - restore `std::function` as callback container (no more single-byte arg nonsense). Still, limit to 1 type per handler type - adds JSON handlers which will receive JsonObject root as both input and output. Same logic as plain - GET returns resource data, PUT updates it. - breaking change to `apiAuthenticate(request)`, it no longer will do `request->send(403)` and expect this to be handled externally. - allow `Api-Key` header containing the key, works for both GET & PUT plain requests. The only way to set apikey for JSON. - add `ApiRequest::param` to retrieve both GET and PUT params (aka args), remove ApiBuffer - remove `API_BUFFER_SIZE`. Allow custom form-data key=value pairs for requests, allow to send basic `String`. - add `API_JSON_BUFFER_SIZE` for the JSON buffer (both input and output) - `/apis` replaced with `/api/list`, no longer uses custom handler and is an `apiRegister` callback - `/api/rpc` custom handler replaced with an `apiRegister` callback WIP further down: - no more `webLog` for API requests, unless `webAccessLog` / `WEB_ACCESS_LOG` is set to `1`. This also needs to happen to the other handlers. - migrate to ArduinoJson v6, since it become apparent it is actually a good upgrade :) - actually make use of JSON endpoints more, right now it's just existing GET for sensors and relays - fork ESPAsyncWebServer to cleanup path parsing and temporary objects attached to the request (also, fix things a lot of things based on PRs there...)
3 years ago
6 years ago
api: rework plain and JSON implementations (#2405) - match paths through a custom AsyncWebHandler instead of using generic not-found fallback handler - allow MQTT-like patterns when registering paths (`simple/path`, `path/+/something`, `path/#`) Replaces `relay/0`, `relay/1` etc. with `relay/+`. Magnitudes are plain paths, but using `/+` in case there's more than 1 magnitude of the same type. - restore `std::function` as callback container (no more single-byte arg nonsense). Still, limit to 1 type per handler type - adds JSON handlers which will receive JsonObject root as both input and output. Same logic as plain - GET returns resource data, PUT updates it. - breaking change to `apiAuthenticate(request)`, it no longer will do `request->send(403)` and expect this to be handled externally. - allow `Api-Key` header containing the key, works for both GET & PUT plain requests. The only way to set apikey for JSON. - add `ApiRequest::param` to retrieve both GET and PUT params (aka args), remove ApiBuffer - remove `API_BUFFER_SIZE`. Allow custom form-data key=value pairs for requests, allow to send basic `String`. - add `API_JSON_BUFFER_SIZE` for the JSON buffer (both input and output) - `/apis` replaced with `/api/list`, no longer uses custom handler and is an `apiRegister` callback - `/api/rpc` custom handler replaced with an `apiRegister` callback WIP further down: - no more `webLog` for API requests, unless `webAccessLog` / `WEB_ACCESS_LOG` is set to `1`. This also needs to happen to the other handlers. - migrate to ArduinoJson v6, since it become apparent it is actually a good upgrade :) - actually make use of JSON endpoints more, right now it's just existing GET for sensors and relays - fork ESPAsyncWebServer to cleanup path parsing and temporary objects attached to the request (also, fix things a lot of things based on PRs there...)
3 years ago
api: rework plain and JSON implementations (#2405) - match paths through a custom AsyncWebHandler instead of using generic not-found fallback handler - allow MQTT-like patterns when registering paths (`simple/path`, `path/+/something`, `path/#`) Replaces `relay/0`, `relay/1` etc. with `relay/+`. Magnitudes are plain paths, but using `/+` in case there's more than 1 magnitude of the same type. - restore `std::function` as callback container (no more single-byte arg nonsense). Still, limit to 1 type per handler type - adds JSON handlers which will receive JsonObject root as both input and output. Same logic as plain - GET returns resource data, PUT updates it. - breaking change to `apiAuthenticate(request)`, it no longer will do `request->send(403)` and expect this to be handled externally. - allow `Api-Key` header containing the key, works for both GET & PUT plain requests. The only way to set apikey for JSON. - add `ApiRequest::param` to retrieve both GET and PUT params (aka args), remove ApiBuffer - remove `API_BUFFER_SIZE`. Allow custom form-data key=value pairs for requests, allow to send basic `String`. - add `API_JSON_BUFFER_SIZE` for the JSON buffer (both input and output) - `/apis` replaced with `/api/list`, no longer uses custom handler and is an `apiRegister` callback - `/api/rpc` custom handler replaced with an `apiRegister` callback WIP further down: - no more `webLog` for API requests, unless `webAccessLog` / `WEB_ACCESS_LOG` is set to `1`. This also needs to happen to the other handlers. - migrate to ArduinoJson v6, since it become apparent it is actually a good upgrade :) - actually make use of JSON endpoints more, right now it's just existing GET for sensors and relays - fork ESPAsyncWebServer to cleanup path parsing and temporary objects attached to the request (also, fix things a lot of things based on PRs there...)
3 years ago
6 years ago
6 years ago
6 years ago
7 years ago
  1. /*
  2. WEBSERVER MODULE
  3. Copyright (C) 2016-2019 by Xose Pérez <xose dot perez at gmail dot com>
  4. */
  5. #include "espurna.h"
  6. #if WEB_SUPPORT
  7. #include <algorithm>
  8. #include <functional>
  9. #include <memory>
  10. #include <Schedule.h>
  11. #include <Print.h>
  12. #include <Hash.h>
  13. #include <FS.h>
  14. #include <ArduinoJson.h>
  15. #include <ESPAsyncWebServer.h>
  16. #include <AsyncJson.h>
  17. #include "ntp.h"
  18. #include "settings.h"
  19. #include "system.h"
  20. #include "utils.h"
  21. #include "web.h"
  22. #if WEB_EMBEDDED
  23. namespace {
  24. #if WEBUI_IMAGE == WEBUI_IMAGE_SMALL
  25. #include "static/index.small.html.gz.h"
  26. #elif WEBUI_IMAGE == WEBUI_IMAGE_LIGHT
  27. #include "static/index.light.html.gz.h"
  28. #elif WEBUI_IMAGE == WEBUI_IMAGE_SENSOR
  29. #include "static/index.sensor.html.gz.h"
  30. #elif WEBUI_IMAGE == WEBUI_IMAGE_RFBRIDGE
  31. #include "static/index.rfbridge.html.gz.h"
  32. #elif WEBUI_IMAGE == WEBUI_IMAGE_RFM69
  33. #include "static/index.rfm69.html.gz.h"
  34. #elif WEBUI_IMAGE == WEBUI_IMAGE_LIGHTFOX
  35. #include "static/index.lightfox.html.gz.h"
  36. #elif WEBUI_IMAGE == WEBUI_IMAGE_GARLAND
  37. #include "static/index.garland.html.gz.h"
  38. #elif WEBUI_IMAGE == WEBUI_IMAGE_THERMOSTAT
  39. #include "static/index.thermostat.html.gz.h"
  40. #elif WEBUI_IMAGE == WEBUI_IMAGE_CURTAIN
  41. #include "static/index.curtain.html.gz.h"
  42. #elif WEBUI_IMAGE == WEBUI_IMAGE_FULL
  43. #include "static/index.all.html.gz.h"
  44. #endif
  45. } // namespace
  46. #endif // WEB_EMBEDDED
  47. #if WEB_SSL_ENABLED
  48. #include "static/server.cer.h"
  49. #include "static/server.key.h"
  50. #endif // WEB_SSL_ENABLED
  51. namespace espurna {
  52. namespace web {
  53. namespace print {
  54. bool RequestPrint::_addBuffer() {
  55. if ((_buffers.size() + 1) > _config.backlog.count) {
  56. if (!_exhaustBuffers()) {
  57. _state = State::Error;
  58. return false;
  59. }
  60. }
  61. // Note: c++17, emplace returns created object reference
  62. // c++11, we need to use .back()
  63. _buffers.emplace_back(_config.backlog.size, 0);
  64. _buffers.back().clear();
  65. return true;
  66. }
  67. // Creates response object that will handle the data written into the Print& interface.
  68. //
  69. // This API expects a **very** careful approach to context switching between SYS and CONT:
  70. // - Returning RESPONSE_TRY_AGAIN before buffers are filled will result in invalid size marker being sent on the wire.
  71. // HTTP client (curl, python requests etc., as discovered in testing) will then drop the connection
  72. // - Returning 0 will immediatly close the connection from our side
  73. // - Calling _prepareRequest() **before** _buffers are filled will result in returning 0
  74. // - Calling yield() / delay() while request handler is active **may** trigger this callback out of sequence
  75. // (e.g. Stream.write(...), Stream.read(...), DEBUG_MSG(...), or any other API trying to switch contexts)
  76. // - Receiving data (tcp ack from the previous packet) **will** trigger the callback when switching contexts.
  77. size_t RequestPrint::_handleRequest(uint8_t* data, size_t maxLen) {
  78. switch (_state) {
  79. case State::None:
  80. return RESPONSE_TRY_AGAIN;
  81. case State::Error:
  82. case State::Done:
  83. return 0;
  84. case State::Sending:
  85. break;
  86. }
  87. size_t written = 0;
  88. while ((written < maxLen) && !_buffers.empty()) {
  89. auto& chunk =_buffers.front();
  90. auto have = maxLen - written;
  91. if (chunk.size() > have) {
  92. std::copy(chunk.data(), chunk.data() + have, data + written);
  93. chunk.erase(chunk.begin(), chunk.begin() + have);
  94. written += have;
  95. } else {
  96. std::copy(chunk.data(), chunk.data() + chunk.size(), data + written);
  97. _buffers.pop_front();
  98. written += chunk.size();
  99. }
  100. }
  101. return written;
  102. }
  103. void RequestPrint::_prepareRequest() {
  104. _state = State::Sending;
  105. auto *response = _request->beginChunkedResponse(
  106. _config.mimeType,
  107. [this](uint8_t*data, size_t maxLen, size_t) -> size_t {
  108. return this->_handleRequest(data, maxLen);
  109. });
  110. response->addHeader(F("Connection"), F("close"));
  111. _request->send(response);
  112. }
  113. size_t RequestPrint::write(uint8_t b) {
  114. return write(&b, 1);
  115. }
  116. bool RequestPrint::_exhaustBuffers() {
  117. // XXX: espasyncwebserver will trigger write callback if we setup response too early
  118. // exploring code, callback handler responds to a special return value RESPONSE_TRY_AGAIN
  119. // but, it seemingly breaks chunked response logic
  120. // XXX: this should be **the only place** that can trigger yield() while we stay in CONT
  121. if (_state == State::None) {
  122. _prepareRequest();
  123. }
  124. using TimeSource = espurna::time::CoreClock;
  125. const auto start = TimeSource::now();
  126. do {
  127. if (TimeSource::now() - start > _config.backlog.timeout) {
  128. _buffers.clear();
  129. break;
  130. }
  131. yield();
  132. } while (!_buffers.empty());
  133. return _buffers.empty();
  134. }
  135. void RequestPrint::flush() {
  136. _exhaustBuffers();
  137. _state = State::Done;
  138. }
  139. size_t RequestPrint::write(const uint8_t* data, size_t size) {
  140. if (_state == State::Error) {
  141. return 0;
  142. }
  143. size_t full_size = size;
  144. auto* data_ptr = data;
  145. while (size) {
  146. if (_buffers.empty() && !_addBuffer()) {
  147. full_size = 0;
  148. break;
  149. }
  150. auto& current = _buffers.back();
  151. const auto have = current.capacity() - current.size();
  152. if (have >= size) {
  153. current.insert(current.end(), data_ptr, data_ptr + size);
  154. size = 0;
  155. } else {
  156. current.insert(current.end(), data_ptr, data_ptr + have);
  157. if (!_addBuffer()) {
  158. full_size = 0;
  159. break;
  160. }
  161. data_ptr += have;
  162. size -= have;
  163. }
  164. }
  165. return full_size;
  166. }
  167. } // namespace print
  168. } // namespace web
  169. } // namespace espurna
  170. // -----------------------------------------------------------------------------
  171. namespace {
  172. PROGMEM_STRING(LastModified, __DATE__ " " __TIME__ " GMT");
  173. static constexpr size_t WebConfigBufferMax { 4096 };
  174. // server instance can't (yet) be static, port is the ctor argument :/
  175. AsyncWebServer* _server;
  176. // XXX shared between requests!
  177. std::vector<uint8_t>* _webConfigBuffer;
  178. bool _webConfigSuccess = false;
  179. // TODO server may not cache the full body
  180. std::vector<web_request_callback_f> _web_request_callbacks;
  181. std::vector<web_body_callback_f> _web_body_callbacks;
  182. } // namespace
  183. // -----------------------------------------------------------------------------
  184. // HOOKS
  185. // -----------------------------------------------------------------------------
  186. namespace {
  187. bool _authenticateRequest(AsyncWebServerRequest* request) {
  188. #if USE_PASSWORD
  189. return request->authenticate(WEB_USERNAME, systemPassword().c_str());
  190. #else
  191. return true;
  192. #endif
  193. }
  194. // Whether the request is for this hostname or IP
  195. bool _isAPModeRequest(AsyncWebServerRequest* request) {
  196. if (wifiConnectable()) {
  197. const auto direct = request->client()->localIP() == wifiApIp();
  198. if (!direct) {
  199. return false;
  200. }
  201. const auto header = request->getHeader(F("Host"));
  202. if (!header) {
  203. return false;
  204. }
  205. const auto host = header->value();
  206. const auto domain = systemHostname() + '.';
  207. const auto ip = wifiApIp().toString();
  208. if (!host.equals(ip) && !host.startsWith(domain)) {
  209. return false;
  210. }
  211. return true;
  212. }
  213. return false;
  214. }
  215. // Allow only requests that use our hostname or IP
  216. bool _onAPModeRequest(AsyncWebServerRequest* request) {
  217. if (wifiConnectable()) {
  218. if (_isAPModeRequest(request)) {
  219. return true;
  220. }
  221. // Immediatly close the connection, ref: https://github.com/xoseperez/espurna/issues/1660
  222. // Not doing so will cause memory exhaustion, because the connection will linger
  223. request->send(404);
  224. request->client()->close();
  225. return false;
  226. }
  227. return true;
  228. }
  229. void _webRequestAuth(AsyncWebServerRequest* request) {
  230. request->requestAuthentication(systemHostname().c_str(), true);
  231. }
  232. void _onReset(AsyncWebServerRequest *request) {
  233. if (!_authenticateRequest(request)) {
  234. _webRequestAuth(request);
  235. return;
  236. }
  237. prepareReset(CustomResetReason::Web);
  238. request->send(200);
  239. }
  240. void _onDiscover(AsyncWebServerRequest *request) {
  241. StaticJsonBuffer<JSON_OBJECT_SIZE(5) + 128> buffer;
  242. JsonObject& root = buffer.createObject();
  243. root["hostname"] = systemHostname();
  244. root["device"] = systemDevice();
  245. const auto app = buildApp();
  246. root["app"] = app.name;
  247. root["version"] = app.version;
  248. auto* response = request->beginResponseStream(
  249. F("application/json"), root.measureLength());
  250. response->setCode(200);
  251. root.printTo(*response);
  252. request->send(response);
  253. }
  254. void _onGetConfig(AsyncWebServerRequest *request) {
  255. if (!_authenticateRequest(request)) {
  256. _webRequestAuth(request);
  257. return;
  258. }
  259. auto out = std::make_shared<String>();
  260. out->reserve(TCP_MSS);
  261. const auto app = buildApp();
  262. char buffer[256];
  263. int prefix_len = snprintf_P(buffer, sizeof(buffer),
  264. PSTR("{\n\"app\": \"%s\",\n\"version\": \"%s\",\n\"backup\": \"1\""),
  265. app.name.c_str(), app.version.c_str());
  266. if (prefix_len <= 0) {
  267. request->send(500);
  268. return;
  269. }
  270. out->concat(buffer, prefix_len);
  271. espurna::settings::foreach([&](espurna::settings::kvs_type::KeyValueResult&& kv) {
  272. auto key = kv.key.read();
  273. auto value = kv.value.read();
  274. int len = snprintf_P(buffer, sizeof(buffer), PSTR("\"%s\": \"%s\""), key.c_str(), value.c_str());
  275. if (len > 0) {
  276. *out += ",\n";
  277. out->concat(buffer, len);
  278. }
  279. });
  280. *out += "\n}";
  281. AsyncWebServerResponse* response = request->beginChunkedResponse(
  282. F("application/json"),
  283. [out](uint8_t* buffer, size_t maxLen, size_t index) -> size_t {
  284. auto len = out->length();
  285. if (index == len) {
  286. return 0;
  287. }
  288. auto* ptr = out->c_str() + index;
  289. size_t have = std::min(len - index, maxLen);
  290. if (have) {
  291. std::copy(ptr, ptr + have, buffer);
  292. }
  293. return have;
  294. });
  295. auto get_timestamp = []() -> String {
  296. #if NTP_SUPPORT
  297. if (ntpSynced()) {
  298. return ntpDateTime();
  299. }
  300. #endif
  301. return String(espurna::time::millis().time_since_epoch().count(), 10);
  302. };
  303. int written = snprintf_P(buffer, sizeof(buffer),
  304. PSTR("attachment; filename=\"%s %s backup.json\""),
  305. systemHostname().c_str(), get_timestamp().c_str());
  306. if (written > 0) {
  307. response->addHeader(F("Content-Disposition"), buffer);
  308. response->addHeader(F("X-XSS-Protection"), F("1; mode=block"));
  309. response->addHeader(F("X-Content-Type-Options"), F("nosniff"));
  310. response->addHeader(F("X-Frame-Options"), F("deny"));
  311. request->send(response);
  312. return;
  313. }
  314. request->send(500);
  315. }
  316. void _onPostConfig(AsyncWebServerRequest *request) {
  317. if (!_authenticateRequest(request)) {
  318. _webRequestAuth(request);
  319. return;
  320. }
  321. request->send(_webConfigSuccess ? 200 : 400);
  322. }
  323. void _onPostConfigFile(AsyncWebServerRequest *request, String, size_t index, uint8_t *data, size_t len, bool final) {
  324. if (!_authenticateRequest(request)) {
  325. _webRequestAuth(request);
  326. return;
  327. }
  328. // No buffer
  329. if (final && (index == 0)) {
  330. _webConfigSuccess = settingsRestoreJson((char*) data);
  331. return;
  332. }
  333. // Buffer start => reset
  334. if (index == 0) if (_webConfigBuffer) delete _webConfigBuffer;
  335. // init buffer if it doesn't exist
  336. if (!_webConfigBuffer) {
  337. _webConfigBuffer = new std::vector<uint8_t>();
  338. _webConfigSuccess = false;
  339. }
  340. // Copy
  341. if (len > 0) {
  342. if ((_webConfigBuffer->size() + len) > std::min(WebConfigBufferMax, systemFreeHeap() - sizeof(std::vector<uint8_t>))) {
  343. delete _webConfigBuffer;
  344. _webConfigBuffer = nullptr;
  345. request->send(500);
  346. return;
  347. }
  348. _webConfigBuffer->reserve(_webConfigBuffer->size() + len);
  349. _webConfigBuffer->insert(_webConfigBuffer->end(), data, data + len);
  350. }
  351. // Ending
  352. if (final) {
  353. _webConfigBuffer->push_back(0);
  354. _webConfigSuccess = settingsRestoreJson((char*) _webConfigBuffer->data());
  355. delete _webConfigBuffer;
  356. }
  357. }
  358. #if WIFI_AP_CAPTIVE_SUPPORT
  359. void _onAPCaptiveRequest(AsyncWebServerRequest* request) {
  360. if (wifiConnectable()) {
  361. auto* response = request->beginResponse(302);
  362. response->addHeader(F("Location"), String(F("http://")) + wifiApIp().toString());
  363. response->addHeader(F("Connection"), F("close"));
  364. request->send(response);
  365. return;
  366. }
  367. request->send(404);
  368. }
  369. #endif
  370. #if WEB_EMBEDDED
  371. PROGMEM_STRING(IfModifiedSince, "If-Modified-Since");
  372. void _onHome(AsyncWebServerRequest *request) {
  373. if (!_isAPModeRequest(request) && !_authenticateRequest(request)) {
  374. _webRequestAuth(request);
  375. return;
  376. }
  377. if (request->hasHeader(FPSTR(IfModifiedSince))) {
  378. const auto value = request->header(FPSTR(IfModifiedSince));
  379. if (strncmp_P(value.c_str(), LastModified, value.length()) == 0) {
  380. request->send(304);
  381. return;
  382. }
  383. }
  384. #if WEB_SSL_ENABLED
  385. // Chunked response, we calculate the chunks based on free heap (in multiples of 32)
  386. // This is necessary when a TLS connection is open since it sucks too much memory
  387. const size_t max = (systemFreeHeap() / 3) & 0xFFE0;
  388. auto* response = request->beginChunkedResponse("text/html", [max](uint8_t *buffer, size_t maxLen, size_t index) -> size_t {
  389. // Get the chunk based on the index and maxLen
  390. size_t len = std::size(webui_image) - index;
  391. len = std::min({len, maxLen, max});
  392. if (len > 0) {
  393. memcpy_P(buffer, webui_image + index, len);
  394. }
  395. // Return the actual length of the chunk (0 for end of file)
  396. return len;
  397. });
  398. #else
  399. auto* response = request->beginResponse_P(200, F("text/html"), webui_image, std::size(webui_image));
  400. #endif
  401. response->addHeader(F("Content-Encoding"), F("gzip"));
  402. response->addHeader(F("Last-Modified"), FPSTR(LastModified));
  403. response->addHeader(F("X-XSS-Protection"), F("1; mode=block"));
  404. response->addHeader(F("X-Content-Type-Options"), F("nosniff"));
  405. response->addHeader(F("X-Frame-Options"), F("deny"));
  406. request->send(response);
  407. }
  408. #endif
  409. #if WEB_SSL_ENABLED
  410. int _onCertificate(void * arg, const char *filename, uint8_t **buf) {
  411. #if WEB_EMBEDDED
  412. if (strcmp(filename, "server.cer") == 0) {
  413. uint8_t * nbuf = (uint8_t*) malloc(server_cer_len);
  414. memcpy_P(nbuf, server_cer, server_cer_len);
  415. *buf = nbuf;
  416. DEBUG_MSG_P(PSTR("[WEB] SSL File: %s - OK\n"), filename);
  417. return server_cer_len;
  418. }
  419. if (strcmp(filename, "server.key") == 0) {
  420. uint8_t * nbuf = (uint8_t*) malloc(server_key_len);
  421. memcpy_P(nbuf, server_key, server_key_len);
  422. *buf = nbuf;
  423. DEBUG_MSG_P(PSTR("[WEB] SSL File: %s - OK\n"), filename);
  424. return server_key_len;
  425. }
  426. DEBUG_MSG_P(PSTR("[WEB] SSL File: %s - ERROR\n"), filename);
  427. *buf = 0;
  428. return 0;
  429. #else
  430. File file = SPIFFS.open(filename, "r");
  431. if (file) {
  432. size_t size = file.size();
  433. uint8_t * nbuf = (uint8_t*) malloc(size);
  434. if (nbuf) {
  435. size = file.read(nbuf, size);
  436. file.close();
  437. *buf = nbuf;
  438. DEBUG_MSG_P(PSTR("[WEB] SSL File: %s - OK\n"), filename);
  439. return size;
  440. }
  441. file.close();
  442. }
  443. DEBUG_MSG_P(PSTR("[WEB] SSL File: %s - ERROR\n"), filename);
  444. *buf = 0;
  445. return 0;
  446. #endif // WEB_EMBEDDED == 1
  447. }
  448. #endif // WEB_SSL_ENABLED
  449. void _onRequest(AsyncWebServerRequest *request){
  450. if (!_onAPModeRequest(request)) return;
  451. // Send request to subscribers, break when request is 'handled' by the callback
  452. for (auto& callback : _web_request_callbacks) {
  453. if (callback(request)) {
  454. return;
  455. }
  456. }
  457. // No subscriber handled the request, return a 404 with implicit "Connection: close"
  458. request->send(404);
  459. // And immediatly close the connection, ref: https://github.com/xoseperez/espurna/issues/1660
  460. // Not doing so will cause memory exhaustion, because the connection will linger
  461. request->client()->close();
  462. }
  463. void _onBody(AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total) {
  464. if (!_onAPModeRequest(request)) return;
  465. // Send request to subscribers
  466. for (unsigned char i = 0; i < _web_body_callbacks.size(); i++) {
  467. bool response = (_web_body_callbacks[i])(request, data, len, index, total);
  468. if (response) return;
  469. }
  470. // Same as _onAPModeRequest(...)
  471. request->send(404);
  472. request->client()->close();
  473. }
  474. } // namespace
  475. bool webApModeRequest(AsyncWebServerRequest* request) {
  476. return _isAPModeRequest(request);
  477. }
  478. bool webAuthenticate(AsyncWebServerRequest *request) {
  479. return _authenticateRequest(request);
  480. }
  481. AsyncWebServer& webServer() {
  482. return *_server;
  483. }
  484. void webBodyRegister(web_body_callback_f callback) {
  485. _web_body_callbacks.push_back(callback);
  486. }
  487. void webRequestRegister(web_request_callback_f callback) {
  488. _web_request_callbacks.push_back(callback);
  489. }
  490. uint16_t webPort() {
  491. #if WEB_SSL_ENABLED
  492. return 443;
  493. #else
  494. constexpr const uint16_t defaultValue(WEB_PORT);
  495. return getSetting("webPort", defaultValue);
  496. #endif
  497. }
  498. void webLog(AsyncWebServerRequest* request) {
  499. const auto client = request->client();
  500. String from = client
  501. ? client->remoteIP().toString()
  502. : F("(unknown)");
  503. DEBUG_MSG_P(PSTR("[WEBSERVER] %s - %s %s\n"),
  504. from.c_str(),
  505. request->methodToString(),
  506. request->url().c_str());
  507. }
  508. class WebAccessLogHandler : public AsyncWebHandler {
  509. bool canHandle(AsyncWebServerRequest* request) override {
  510. webLog(request);
  511. return false;
  512. }
  513. };
  514. void webSetup() {
  515. // Create server and install global URL debug handler
  516. // (since we don't want to forcibly add it to each instance)
  517. unsigned int port = webPort();
  518. _server = new AsyncWebServer(port);
  519. #if DEBUG_SUPPORT
  520. if (getSetting("webAccessLog", (1 == WEB_ACCESS_LOG))) {
  521. static WebAccessLogHandler log;
  522. _server->addHandler(&log);
  523. }
  524. #endif
  525. // Rewrites
  526. _server->rewrite("/", "/index.html");
  527. // Serve home (basic authentication protection is done manually b/c the handler is installed through callback functions)
  528. #if WEB_EMBEDDED
  529. _server->on("/index.html", HTTP_GET, _onHome);
  530. #endif
  531. // Serve static files (not supported, yet)
  532. #if SPIFFS_SUPPORT
  533. _server->serveStatic("/", SPIFFS, "/")
  534. .setLastModified(_last_modified)
  535. .setFilter([](AsyncWebServerRequest *request) -> bool {
  536. webLog(request);
  537. return true;
  538. });
  539. #endif
  540. _server->on("/reset", HTTP_GET, _onReset);
  541. _server->on("/config", HTTP_GET, _onGetConfig);
  542. _server->on("/config", HTTP_POST | HTTP_PUT, _onPostConfig, _onPostConfigFile);
  543. _server->on("/discover", HTTP_GET, _onDiscover);
  544. #if WIFI_AP_CAPTIVE_SUPPORT
  545. _server->on("/generate_204", _onAPCaptiveRequest);
  546. _server->on("/fwlink", _onAPCaptiveRequest);
  547. #endif
  548. // Handle every other request, including 404
  549. _server->onRequestBody(_onBody);
  550. _server->onNotFound(_onRequest);
  551. // Run server
  552. #if WEB_SSL_ENABLED
  553. _server->onSslFileRequest(_onCertificate, NULL);
  554. _server->beginSecure("server.cer", "server.key", NULL);
  555. #else
  556. _server->begin();
  557. #endif
  558. DEBUG_MSG_P(PSTR("[WEBSERVER] Webserver running on port %u\n"), port);
  559. }
  560. #endif // WEB_SUPPORT