diff --git a/code/espurna/web.cpp b/code/espurna/web.cpp index 3ee04ffa..59e41aff 100644 --- a/code/espurna/web.cpp +++ b/code/espurna/web.cpp @@ -71,6 +71,8 @@ namespace { namespace build { +STRING_VIEW_INLINE(WebRemoteDomain, WEB_REMOTE_DOMAIN); + constexpr auto DefaultPort = uint16_t{ #if WEB_SSL_ENABLED 443 @@ -96,19 +98,24 @@ namespace keys { STRING_VIEW_INLINE(Prefix, "web"); -STRING_VIEW_INLINE(Port, "webPort"); STRING_VIEW_INLINE(AccessLog, "webAccessLog"); +STRING_VIEW_INLINE(Domain, "webDomain"); +STRING_VIEW_INLINE(Port, "webPort"); } // namespace keys -uint16_t port() { - return getSetting(keys::Port, build::port()); -} - bool access_log() { return getSetting(keys::AccessLog, build::access_log()); } +String domain() { + return getSetting(keys::Domain, build::WebRemoteDomain); +} + +uint16_t port() { + return getSetting(keys::Port, build::port()); +} + } // namespace settings } // namespace @@ -368,6 +375,16 @@ void _onDiscover(AsyncWebServerRequest *request) { request->send(response); } +void _setupAccessControlHeaders() { + const auto domain = espurna::web::settings::domain(); + + auto& headers = DefaultHeaders::Instance(); + headers.addHeader(F("Access-Control-Allow-Origin"), domain); + if (!domain.equals("*")) { + headers.addHeader(F("Access-Control-Allow-Credentials"), F("true")); + } +} + void _addSecurityHeaders(AsyncWebServerResponse* response) { response->addHeader(F("X-XSS-Protection"), F("1; mode=block")); response->addHeader(F("X-Content-Type-Options"), F("nosniff")); @@ -649,6 +666,10 @@ void _onBody(AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t i } +void _onVisible(JsonObject& root) { + root[espurna::web::settings::keys::Port] = _port; +} + bool _onKeyCheck(espurna::StringView key, const JsonVariant& value) { return key.startsWith(espurna::web::settings::keys::Prefix); } @@ -759,8 +780,12 @@ void webSetup() { DEBUG_MSG_P(PSTR("[WEBSERVER] Webserver running on port %hu\n"), _port); + // CORS setup + _setupAccessControlHeaders(); + // Handle ws server settings updates wsRegister() + .onVisible(_onVisible) .onKeyCheck(_onKeyCheck); } diff --git a/code/espurna/ws.cpp b/code/espurna/ws.cpp index 104707a8..46a113b5 100644 --- a/code/espurna/ws.cpp +++ b/code/espurna/ws.cpp @@ -42,16 +42,26 @@ STRING_VIEW_INLINE(SchemaKey, "schema"); namespace build { -constexpr uint16_t port() { - return WEB_PORT; -} - constexpr bool authentication() { return 1 == WS_AUTHENTICATION; } } // namespace build +namespace settings { +namespace keys { + +STRING_VIEW_INLINE(Prefix, "ws"); +STRING_VIEW_INLINE(Auth, "wsAuth"); + +} // namespace keys + +bool authentication() { + return getSetting(keys::Auth, build::authentication()); +} + +} // namespace settings + } // namespace EnumerableConfig::EnumerableConfig(JsonObject& root, StringView name) : @@ -107,7 +117,7 @@ EnumerablePayload::EnumerablePayload(JsonObject& root, StringView name) : _root(root.createNestedObject(name)) {} -void EnumerablePayload::operator()(StringView name, settings::Iota iota, Check check, Pairs&& pairs) { +void EnumerablePayload::operator()(StringView name, espurna::settings::Iota iota, Check check, Pairs&& pairs) { JsonArray& entries = _root.createNestedArray(name); if (_root.containsKey(internal::SchemaKey)) { @@ -154,10 +164,6 @@ void EnumerableTypes::operator()(int value, StringView text) { namespace { -template -struct BaseTimeFormat { -}; - void _wsUpdateAp(JsonObject& root) { IPAddress ip{}; @@ -245,6 +251,8 @@ void _wsDoUpdate(const bool connected) { namespace { +bool _ws_auth { espurna::web::ws::build::authentication() }; + AsyncWebSocket _ws("/ws"); std::queue _ws_queue; ws_callbacks_t _ws_callbacks; @@ -668,7 +676,7 @@ void _wsParse(AsyncWebSocketClient* client, uint8_t* payload, size_t length) { } bool _wsOnKeyCheck(espurna::StringView key, const JsonVariant&) { - return key.startsWith(STRING_VIEW("ws")); + return key.startsWith(espurna::web::ws::settings::keys::Prefix); } void _wsOnConnected(JsonObject& root) { @@ -696,9 +704,10 @@ void _wsOnConnected(JsonObject& root) { root[F("sketch_size")] = ESP.getSketchSize(); root[F("free_size")] = ESP.getFreeSketchSpace(); +} - root[F("webPort")] = getSetting(F("webPort"), espurna::web::ws::build::port()); - root[F("wsAuth")] = getSetting(F("wsAuth"), espurna::web::ws::build::authentication()); +void _wsOnVisible(JsonObject& root) { + root[espurna::web::ws::settings::keys::Auth] = _ws_auth ? 1 : 0; } void _wsConnected(uint32_t client_id) { @@ -726,7 +735,7 @@ void _wsEvent(AsyncWebSocket* server, AsyncWebSocketClient* client, AwsEventType { const auto ip = client->remoteIP().toString(); #ifndef NOWSAUTH - if (!_wsAuth(client)) { + if (_ws_auth && !_wsAuth(client)) { DEBUG_MSG_P(PSTR("[WEBSOCKET] #%u session expired for %s\n"), client->id(), ip.c_str()); client->close(); @@ -936,21 +945,15 @@ void wsSend(uint32_t client_id, const char * payload) { } void wsSetup() { - _ws.onEvent(_wsEvent); webServer().addHandler(&_ws); - // CORS - const String webDomain = getSetting(F("webDomain"), F(WEB_REMOTE_DOMAIN)); - DefaultHeaders::Instance().addHeader(F("Access-Control-Allow-Origin"), webDomain); - if (!webDomain.equals("*")) { - DefaultHeaders::Instance().addHeader(F("Access-Control-Allow-Credentials"), F("true")); - } - + _ws_auth = espurna::web::ws::settings::authentication(); webServer().on("/auth", HTTP_GET, _onAuth); wsRegister() .onConnected(_wsOnConnected) + .onVisible(_wsOnVisible) .onKeyCheck(_wsOnKeyCheck); espurnaRegisterLoop(_wsLoop);