1
0
mirror of https://github.com/esphome/esphome.git synced 2025-09-19 11:42:20 +01:00
This commit is contained in:
J. Nick Koston
2025-09-05 11:15:09 -05:00
parent 9b882a8792
commit c8d575aab7

View File

@@ -39,45 +39,12 @@ namespace web_server {
static const char *const TAG = "web_server"; static const char *const TAG = "web_server";
#ifdef USE_ESP8266
// Common strings used multiple times - deduplicated in PROGMEM on ESP8266
static const char CONTENT_TYPE_JSON[] PROGMEM = "application/json";
static const char CONTENT_TYPE_HTML[] PROGMEM = "text/html";
static const char CONTENT_TYPE_CSS[] PROGMEM = "text/css";
static const char CONTENT_TYPE_JS[] PROGMEM = "text/javascript";
static const char CONTENT_TYPE_PLAIN[] PROGMEM = "text/plain";
static const char HEADER_CONTENT_ENCODING[] PROGMEM = "Content-Encoding";
static const char ENCODING_GZIP[] PROGMEM = "gzip";
static const char EVENT_STATE[] PROGMEM = "state";
static const char MSG_NOT_FOUND[] PROGMEM = "Not Found";
#else
// For all other platforms, regular string constants
static const char *const CONTENT_TYPE_JSON = "application/json";
static const char *const CONTENT_TYPE_HTML = "text/html";
static const char *const CONTENT_TYPE_CSS = "text/css";
static const char *const CONTENT_TYPE_JS = "text/javascript";
static const char *const CONTENT_TYPE_PLAIN = "text/plain";
static const char *const HEADER_CONTENT_ENCODING = "Content-Encoding";
static const char *const ENCODING_GZIP = "gzip";
static const char *const EVENT_STATE = "state";
static const char *const MSG_NOT_FOUND = "Not Found";
#endif
#ifdef USE_WEBSERVER_PRIVATE_NETWORK_ACCESS #ifdef USE_WEBSERVER_PRIVATE_NETWORK_ACCESS
#ifdef USE_ESP8266
// Store single copy in PROGMEM on ESP8266
static const char HEADER_PNA_NAME[] PROGMEM = "Private-Network-Access-Name";
static const char HEADER_PNA_ID[] PROGMEM = "Private-Network-Access-ID";
static const char HEADER_CORS_REQ_PNA[] PROGMEM = "Access-Control-Request-Private-Network";
static const char HEADER_CORS_ALLOW_PNA[] PROGMEM = "Access-Control-Allow-Private-Network";
#else
// For all other platforms, use regular strings
static const char *const HEADER_PNA_NAME = "Private-Network-Access-Name"; static const char *const HEADER_PNA_NAME = "Private-Network-Access-Name";
static const char *const HEADER_PNA_ID = "Private-Network-Access-ID"; static const char *const HEADER_PNA_ID = "Private-Network-Access-ID";
static const char *const HEADER_CORS_REQ_PNA = "Access-Control-Request-Private-Network"; static const char *const HEADER_CORS_REQ_PNA = "Access-Control-Request-Private-Network";
static const char *const HEADER_CORS_ALLOW_PNA = "Access-Control-Allow-Private-Network"; static const char *const HEADER_CORS_ALLOW_PNA = "Access-Control-Allow-Private-Network";
#endif #endif
#endif
// Parse URL and return match info // Parse URL and return match info
static UrlMatch match_url(const char *url_ptr, size_t url_len, bool only_domain) { static UrlMatch match_url(const char *url_ptr, size_t url_len, bool only_domain) {
@@ -155,7 +122,7 @@ void DeferredUpdateEventSource::process_deferred_queue_() {
while (!deferred_queue_.empty()) { while (!deferred_queue_.empty()) {
DeferredEvent &de = deferred_queue_.front(); DeferredEvent &de = deferred_queue_.front();
std::string message = de.message_generator_(web_server_, de.source_); std::string message = de.message_generator_(web_server_, de.source_);
if (this->send(message.c_str(), EVENT_STATE) != DISCARDED) { if (this->send(message.c_str(), "state") != DISCARDED) {
// O(n) but memory efficiency is more important than speed here which is why std::vector was chosen // O(n) but memory efficiency is more important than speed here which is why std::vector was chosen
deferred_queue_.erase(deferred_queue_.begin()); deferred_queue_.erase(deferred_queue_.begin());
this->consecutive_send_failures_ = 0; // Reset failure count on successful send this->consecutive_send_failures_ = 0; // Reset failure count on successful send
@@ -204,7 +171,7 @@ void DeferredUpdateEventSource::deferrable_send_state(void *source, const char *
deq_push_back_with_dedup_(source, message_generator); deq_push_back_with_dedup_(source, message_generator);
} else { } else {
std::string message = message_generator(web_server_, source); std::string message = message_generator(web_server_, source);
if (this->send(message.c_str(), EVENT_STATE) == DISCARDED) { if (this->send(message.c_str(), "state") == DISCARDED) {
deq_push_back_with_dedup_(source, message_generator); deq_push_back_with_dedup_(source, message_generator);
} else { } else {
this->consecutive_send_failures_ = 0; // Reset failure count on successful send this->consecutive_send_failures_ = 0; // Reset failure count on successful send
@@ -239,7 +206,7 @@ void DeferredUpdateEventSourceList::try_send_nodefer(const char *message, const
} }
void DeferredUpdateEventSourceList::add_new_client(WebServer *ws, AsyncWebServerRequest *request) { void DeferredUpdateEventSourceList::add_new_client(WebServer *ws, AsyncWebServerRequest *request) {
DeferredUpdateEventSource *es = new DeferredUpdateEventSource(ws, F("/events")); DeferredUpdateEventSource *es = new DeferredUpdateEventSource(ws, "/events");
this->push_back(es); this->push_back(es);
es->onConnect([this, ws, es](AsyncEventSourceClient *client) { es->onConnect([this, ws, es](AsyncEventSourceClient *client) {
@@ -349,21 +316,21 @@ float WebServer::get_setup_priority() const { return setup_priority::WIFI - 1.0f
#ifdef USE_WEBSERVER_LOCAL #ifdef USE_WEBSERVER_LOCAL
void WebServer::handle_index_request(AsyncWebServerRequest *request) { void WebServer::handle_index_request(AsyncWebServerRequest *request) {
#ifndef USE_ESP8266 #ifndef USE_ESP8266
AsyncWebServerResponse *response = request->beginResponse(200, CONTENT_TYPE_HTML, INDEX_GZ, sizeof(INDEX_GZ)); AsyncWebServerResponse *response = request->beginResponse(200, "text/html", INDEX_GZ, sizeof(INDEX_GZ));
#else #else
AsyncWebServerResponse *response = request->beginResponse_P(200, CONTENT_TYPE_HTML, INDEX_GZ, sizeof(INDEX_GZ)); AsyncWebServerResponse *response = request->beginResponse_P(200, "text/html", INDEX_GZ, sizeof(INDEX_GZ));
#endif #endif
response->addHeader(HEADER_CONTENT_ENCODING, ENCODING_GZIP); response->addHeader("Content-Encoding", "gzip");
request->send(response); request->send(response);
} }
#elif USE_WEBSERVER_VERSION >= 2 #elif USE_WEBSERVER_VERSION >= 2
void WebServer::handle_index_request(AsyncWebServerRequest *request) { void WebServer::handle_index_request(AsyncWebServerRequest *request) {
#ifndef USE_ESP8266 #ifndef USE_ESP8266
AsyncWebServerResponse *response = AsyncWebServerResponse *response =
request->beginResponse(200, CONTENT_TYPE_HTML, ESPHOME_WEBSERVER_INDEX_HTML, ESPHOME_WEBSERVER_INDEX_HTML_SIZE); request->beginResponse(200, "text/html", ESPHOME_WEBSERVER_INDEX_HTML, ESPHOME_WEBSERVER_INDEX_HTML_SIZE);
#else #else
AsyncWebServerResponse *response = AsyncWebServerResponse *response =
request->beginResponse_P(200, CONTENT_TYPE_HTML, ESPHOME_WEBSERVER_INDEX_HTML, ESPHOME_WEBSERVER_INDEX_HTML_SIZE); request->beginResponse_P(200, "text/html", ESPHOME_WEBSERVER_INDEX_HTML, ESPHOME_WEBSERVER_INDEX_HTML_SIZE);
#endif #endif
// No gzip header here because the HTML file is so small // No gzip header here because the HTML file is so small
request->send(response); request->send(response);
@@ -372,8 +339,8 @@ void WebServer::handle_index_request(AsyncWebServerRequest *request) {
#ifdef USE_WEBSERVER_PRIVATE_NETWORK_ACCESS #ifdef USE_WEBSERVER_PRIVATE_NETWORK_ACCESS
void WebServer::handle_pna_cors_request(AsyncWebServerRequest *request) { void WebServer::handle_pna_cors_request(AsyncWebServerRequest *request) {
AsyncWebServerResponse *response = request->beginResponse(200, F("")); AsyncWebServerResponse *response = request->beginResponse(200, "");
response->addHeader(HEADER_CORS_ALLOW_PNA, F("true")); response->addHeader(HEADER_CORS_ALLOW_PNA, "true");
response->addHeader(HEADER_PNA_NAME, App.get_name().c_str()); response->addHeader(HEADER_PNA_NAME, App.get_name().c_str());
std::string mac = get_mac_address_pretty(); std::string mac = get_mac_address_pretty();
response->addHeader(HEADER_PNA_ID, mac.c_str()); response->addHeader(HEADER_PNA_ID, mac.c_str());
@@ -385,12 +352,12 @@ void WebServer::handle_pna_cors_request(AsyncWebServerRequest *request) {
void WebServer::handle_css_request(AsyncWebServerRequest *request) { void WebServer::handle_css_request(AsyncWebServerRequest *request) {
#ifndef USE_ESP8266 #ifndef USE_ESP8266
AsyncWebServerResponse *response = AsyncWebServerResponse *response =
request->beginResponse(200, CONTENT_TYPE_CSS, ESPHOME_WEBSERVER_CSS_INCLUDE, ESPHOME_WEBSERVER_CSS_INCLUDE_SIZE); request->beginResponse(200, "text/css", ESPHOME_WEBSERVER_CSS_INCLUDE, ESPHOME_WEBSERVER_CSS_INCLUDE_SIZE);
#else #else
AsyncWebServerResponse *response = request->beginResponse_P(200, CONTENT_TYPE_CSS, ESPHOME_WEBSERVER_CSS_INCLUDE, AsyncWebServerResponse *response =
ESPHOME_WEBSERVER_CSS_INCLUDE_SIZE); request->beginResponse_P(200, "text/css", ESPHOME_WEBSERVER_CSS_INCLUDE, ESPHOME_WEBSERVER_CSS_INCLUDE_SIZE);
#endif #endif
response->addHeader(HEADER_CONTENT_ENCODING, ENCODING_GZIP); response->addHeader("Content-Encoding", "gzip");
request->send(response); request->send(response);
} }
#endif #endif
@@ -399,12 +366,12 @@ void WebServer::handle_css_request(AsyncWebServerRequest *request) {
void WebServer::handle_js_request(AsyncWebServerRequest *request) { void WebServer::handle_js_request(AsyncWebServerRequest *request) {
#ifndef USE_ESP8266 #ifndef USE_ESP8266
AsyncWebServerResponse *response = AsyncWebServerResponse *response =
request->beginResponse(200, CONTENT_TYPE_JS, ESPHOME_WEBSERVER_JS_INCLUDE, ESPHOME_WEBSERVER_JS_INCLUDE_SIZE); request->beginResponse(200, "text/javascript", ESPHOME_WEBSERVER_JS_INCLUDE, ESPHOME_WEBSERVER_JS_INCLUDE_SIZE);
#else #else
AsyncWebServerResponse *response = AsyncWebServerResponse *response =
request->beginResponse_P(200, CONTENT_TYPE_JS, ESPHOME_WEBSERVER_JS_INCLUDE, ESPHOME_WEBSERVER_JS_INCLUDE_SIZE); request->beginResponse_P(200, "text/javascript", ESPHOME_WEBSERVER_JS_INCLUDE, ESPHOME_WEBSERVER_JS_INCLUDE_SIZE);
#endif #endif
response->addHeader(HEADER_CONTENT_ENCODING, ENCODING_GZIP); response->addHeader("Content-Encoding", "gzip");
request->send(response); request->send(response);
} }
#endif #endif
@@ -455,7 +422,7 @@ void WebServer::handle_sensor_request(AsyncWebServerRequest *request, const UrlM
if (request->method() == HTTP_GET && match.method_empty()) { if (request->method() == HTTP_GET && match.method_empty()) {
auto detail = get_request_detail(request); auto detail = get_request_detail(request);
std::string data = this->sensor_json(obj, obj->state, detail); std::string data = this->sensor_json(obj, obj->state, detail);
request->send(200, CONTENT_TYPE_JSON, data.c_str()); request->send(200, "application/json", data.c_str());
return; return;
} }
} }
@@ -500,7 +467,7 @@ void WebServer::handle_text_sensor_request(AsyncWebServerRequest *request, const
if (request->method() == HTTP_GET && match.method_empty()) { if (request->method() == HTTP_GET && match.method_empty()) {
auto detail = get_request_detail(request); auto detail = get_request_detail(request);
std::string data = this->text_sensor_json(obj, obj->state, detail); std::string data = this->text_sensor_json(obj, obj->state, detail);
request->send(200, CONTENT_TYPE_JSON, data.c_str()); request->send(200, "application/json", data.c_str());
return; return;
} }
} }
@@ -539,7 +506,7 @@ void WebServer::handle_switch_request(AsyncWebServerRequest *request, const UrlM
if (request->method() == HTTP_GET && match.method_empty()) { if (request->method() == HTTP_GET && match.method_empty()) {
auto detail = get_request_detail(request); auto detail = get_request_detail(request);
std::string data = this->switch_json(obj, obj->state, detail); std::string data = this->switch_json(obj, obj->state, detail);
request->send(200, CONTENT_TYPE_JSON, data.c_str()); request->send(200, "application/json", data.c_str());
return; return;
} }
@@ -604,7 +571,7 @@ void WebServer::handle_button_request(AsyncWebServerRequest *request, const UrlM
if (request->method() == HTTP_GET && match.method_empty()) { if (request->method() == HTTP_GET && match.method_empty()) {
auto detail = get_request_detail(request); auto detail = get_request_detail(request);
std::string data = this->button_json(obj, detail); std::string data = this->button_json(obj, detail);
request->send(200, CONTENT_TYPE_JSON, data.c_str()); request->send(200, "application/json", data.c_str());
} else if (match.method_equals("press")) { } else if (match.method_equals("press")) {
this->defer([obj]() { obj->press(); }); this->defer([obj]() { obj->press(); });
request->send(200); request->send(200);
@@ -645,7 +612,7 @@ void WebServer::handle_binary_sensor_request(AsyncWebServerRequest *request, con
if (request->method() == HTTP_GET && match.method_empty()) { if (request->method() == HTTP_GET && match.method_empty()) {
auto detail = get_request_detail(request); auto detail = get_request_detail(request);
std::string data = this->binary_sensor_json(obj, obj->state, detail); std::string data = this->binary_sensor_json(obj, obj->state, detail);
request->send(200, CONTENT_TYPE_JSON, data.c_str()); request->send(200, "application/json", data.c_str());
return; return;
} }
} }
@@ -684,7 +651,7 @@ void WebServer::handle_fan_request(AsyncWebServerRequest *request, const UrlMatc
if (request->method() == HTTP_GET && match.method_empty()) { if (request->method() == HTTP_GET && match.method_empty()) {
auto detail = get_request_detail(request); auto detail = get_request_detail(request);
std::string data = this->fan_json(obj, detail); std::string data = this->fan_json(obj, detail);
request->send(200, CONTENT_TYPE_JSON, data.c_str()); request->send(200, "application/json", data.c_str());
} else if (match.method_equals("toggle")) { } else if (match.method_equals("toggle")) {
this->defer([obj]() { obj->toggle().perform(); }); this->defer([obj]() { obj->toggle().perform(); });
request->send(200); request->send(200);
@@ -758,7 +725,7 @@ void WebServer::handle_light_request(AsyncWebServerRequest *request, const UrlMa
if (request->method() == HTTP_GET && match.method_empty()) { if (request->method() == HTTP_GET && match.method_empty()) {
auto detail = get_request_detail(request); auto detail = get_request_detail(request);
std::string data = this->light_json(obj, detail); std::string data = this->light_json(obj, detail);
request->send(200, CONTENT_TYPE_JSON, data.c_str()); request->send(200, "application/json", data.c_str());
} else if (match.method_equals("toggle")) { } else if (match.method_equals("toggle")) {
this->defer([obj]() { obj->toggle().perform(); }); this->defer([obj]() { obj->toggle().perform(); });
request->send(200); request->send(200);
@@ -831,7 +798,7 @@ void WebServer::handle_cover_request(AsyncWebServerRequest *request, const UrlMa
if (request->method() == HTTP_GET && match.method_empty()) { if (request->method() == HTTP_GET && match.method_empty()) {
auto detail = get_request_detail(request); auto detail = get_request_detail(request);
std::string data = this->cover_json(obj, detail); std::string data = this->cover_json(obj, detail);
request->send(200, CONTENT_TYPE_JSON, data.c_str()); request->send(200, "application/json", data.c_str());
return; return;
} }
@@ -902,7 +869,7 @@ void WebServer::handle_number_request(AsyncWebServerRequest *request, const UrlM
if (request->method() == HTTP_GET && match.method_empty()) { if (request->method() == HTTP_GET && match.method_empty()) {
auto detail = get_request_detail(request); auto detail = get_request_detail(request);
std::string data = this->number_json(obj, obj->state, detail); std::string data = this->number_json(obj, obj->state, detail);
request->send(200, CONTENT_TYPE_JSON, data.c_str()); request->send(200, "application/json", data.c_str());
return; return;
} }
if (!match.method_equals("set")) { if (!match.method_equals("set")) {
@@ -968,7 +935,7 @@ void WebServer::handle_date_request(AsyncWebServerRequest *request, const UrlMat
if (request->method() == HTTP_GET && match.method_empty()) { if (request->method() == HTTP_GET && match.method_empty()) {
auto detail = get_request_detail(request); auto detail = get_request_detail(request);
std::string data = this->date_json(obj, detail); std::string data = this->date_json(obj, detail);
request->send(200, CONTENT_TYPE_JSON, data.c_str()); request->send(200, "application/json", data.c_str());
return; return;
} }
if (!match.method_equals("set")) { if (!match.method_equals("set")) {
@@ -1024,7 +991,7 @@ void WebServer::handle_time_request(AsyncWebServerRequest *request, const UrlMat
if (request->method() == HTTP_GET && match.method_empty()) { if (request->method() == HTTP_GET && match.method_empty()) {
auto detail = get_request_detail(request); auto detail = get_request_detail(request);
std::string data = this->time_json(obj, detail); std::string data = this->time_json(obj, detail);
request->send(200, CONTENT_TYPE_JSON, data.c_str()); request->send(200, "application/json", data.c_str());
return; return;
} }
if (!match.method_equals("set")) { if (!match.method_equals("set")) {
@@ -1079,7 +1046,7 @@ void WebServer::handle_datetime_request(AsyncWebServerRequest *request, const Ur
if (request->method() == HTTP_GET && match.method_empty()) { if (request->method() == HTTP_GET && match.method_empty()) {
auto detail = get_request_detail(request); auto detail = get_request_detail(request);
std::string data = this->datetime_json(obj, detail); std::string data = this->datetime_json(obj, detail);
request->send(200, CONTENT_TYPE_JSON, data.c_str()); request->send(200, "application/json", data.c_str());
return; return;
} }
if (!match.method_equals("set")) { if (!match.method_equals("set")) {
@@ -1136,7 +1103,7 @@ void WebServer::handle_text_request(AsyncWebServerRequest *request, const UrlMat
if (request->method() == HTTP_GET && match.method_empty()) { if (request->method() == HTTP_GET && match.method_empty()) {
auto detail = get_request_detail(request); auto detail = get_request_detail(request);
std::string data = this->text_json(obj, obj->state, detail); std::string data = this->text_json(obj, obj->state, detail);
request->send(200, CONTENT_TYPE_JSON, data.c_str()); request->send(200, "application/json", data.c_str());
return; return;
} }
if (!match.method_equals("set")) { if (!match.method_equals("set")) {
@@ -1194,7 +1161,7 @@ void WebServer::handle_select_request(AsyncWebServerRequest *request, const UrlM
if (request->method() == HTTP_GET && match.method_empty()) { if (request->method() == HTTP_GET && match.method_empty()) {
auto detail = get_request_detail(request); auto detail = get_request_detail(request);
std::string data = this->select_json(obj, obj->state, detail); std::string data = this->select_json(obj, obj->state, detail);
request->send(200, CONTENT_TYPE_JSON, data.c_str()); request->send(200, "application/json", data.c_str());
return; return;
} }
@@ -1249,7 +1216,7 @@ void WebServer::handle_climate_request(AsyncWebServerRequest *request, const Url
if (request->method() == HTTP_GET && match.method_empty()) { if (request->method() == HTTP_GET && match.method_empty()) {
auto detail = get_request_detail(request); auto detail = get_request_detail(request);
std::string data = this->climate_json(obj, detail); std::string data = this->climate_json(obj, detail);
request->send(200, CONTENT_TYPE_JSON, data.c_str()); request->send(200, "application/json", data.c_str());
return; return;
} }
@@ -1387,7 +1354,7 @@ void WebServer::handle_lock_request(AsyncWebServerRequest *request, const UrlMat
if (request->method() == HTTP_GET && match.method_empty()) { if (request->method() == HTTP_GET && match.method_empty()) {
auto detail = get_request_detail(request); auto detail = get_request_detail(request);
std::string data = this->lock_json(obj, obj->state, detail); std::string data = this->lock_json(obj, obj->state, detail);
request->send(200, CONTENT_TYPE_JSON, data.c_str()); request->send(200, "application/json", data.c_str());
return; return;
} }
@@ -1458,7 +1425,7 @@ void WebServer::handle_valve_request(AsyncWebServerRequest *request, const UrlMa
if (request->method() == HTTP_GET && match.method_empty()) { if (request->method() == HTTP_GET && match.method_empty()) {
auto detail = get_request_detail(request); auto detail = get_request_detail(request);
std::string data = this->valve_json(obj, detail); std::string data = this->valve_json(obj, detail);
request->send(200, CONTENT_TYPE_JSON, data.c_str()); request->send(200, "application/json", data.c_str());
return; return;
} }
@@ -1525,7 +1492,7 @@ void WebServer::handle_alarm_control_panel_request(AsyncWebServerRequest *reques
if (request->method() == HTTP_GET && match.method_empty()) { if (request->method() == HTTP_GET && match.method_empty()) {
auto detail = get_request_detail(request); auto detail = get_request_detail(request);
std::string data = this->alarm_control_panel_json(obj, obj->get_state(), detail); std::string data = this->alarm_control_panel_json(obj, obj->get_state(), detail);
request->send(200, CONTENT_TYPE_JSON, data.c_str()); request->send(200, "application/json", data.c_str());
return; return;
} }
@@ -1590,7 +1557,7 @@ void WebServer::handle_event_request(AsyncWebServerRequest *request, const UrlMa
if (request->method() == HTTP_GET && match.method_empty()) { if (request->method() == HTTP_GET && match.method_empty()) {
auto detail = get_request_detail(request); auto detail = get_request_detail(request);
std::string data = this->event_json(obj, "", detail); std::string data = this->event_json(obj, "", detail);
request->send(200, CONTENT_TYPE_JSON, data.c_str()); request->send(200, "application/json", data.c_str());
return; return;
} }
} }
@@ -1654,7 +1621,7 @@ void WebServer::handle_update_request(AsyncWebServerRequest *request, const UrlM
if (request->method() == HTTP_GET && match.method_empty()) { if (request->method() == HTTP_GET && match.method_empty()) {
auto detail = get_request_detail(request); auto detail = get_request_detail(request);
std::string data = this->update_json(obj, detail); std::string data = this->update_json(obj, detail);
request->send(200, CONTENT_TYPE_JSON, data.c_str()); request->send(200, "application/json", data.c_str());
return; return;
} }
@@ -1940,7 +1907,7 @@ void WebServer::handleRequest(AsyncWebServerRequest *request) {
// No matching handler found - send 404 // No matching handler found - send 404
ESP_LOGV(TAG, "Request for unknown URL: %s", url.c_str()); ESP_LOGV(TAG, "Request for unknown URL: %s", url.c_str());
request->send(404, CONTENT_TYPE_PLAIN, MSG_NOT_FOUND); // NOLINT(readability-suspicious-call-argument) request->send(404, "text/plain", "Not Found");
} }
bool WebServer::isRequestHandlerTrivial() const { return false; } bool WebServer::isRequestHandlerTrivial() const { return false; }