1
0
mirror of https://github.com/esphome/esphome.git synced 2026-02-08 00:31:58 +00:00

[captive_portal] Allow web_server access while captive portal is active

This commit is contained in:
J. Nick Koston
2026-01-08 15:42:05 -10:00
parent 012a1e2afd
commit 10ed44165d
5 changed files with 40 additions and 11 deletions

View File

@@ -43,7 +43,11 @@ void CaptivePortal::handle_config(AsyncWebServerRequest *request) {
scan.get_with_auth());
#endif
}
#ifdef USE_WEBSERVER
stream->print(ESPHOME_F("],\"web_server\":true}"));
#else
stream->print(ESPHOME_F("]}"));
#endif
request->send(stream);
}
void CaptivePortal::handle_wifisave(AsyncWebServerRequest *request) {
@@ -71,7 +75,8 @@ void CaptivePortal::setup() {
void CaptivePortal::start() {
this->base_->init();
if (!this->initialized_) {
this->base_->add_handler(this);
// Use fallback position so web_server handlers are checked first
this->base_->add_handler(this, web_server_base::HandlerPosition::FALLBACK);
}
network::IPAddress ip = wifi::global_wifi_component->wifi_soft_ap_ip();

View File

@@ -46,9 +46,10 @@ class CaptivePortal : public AsyncWebHandler, public Component {
}
bool canHandle(AsyncWebServerRequest *request) const override {
// Handle all GET requests when captive portal is active
// Handle all GET requests when captive portal is active.
// This allows us to respond with the portal page for any URL,
// triggering OS captive portal detection
// triggering OS captive portal detection.
// We use add_fallback_handler() so web_server handlers are checked first.
return this->active_ && request->method() == HTTP_GET;
}

View File

@@ -28,6 +28,10 @@
#include "esphome/components/climate/climate.h"
#endif
#ifdef USE_CAPTIVE_PORTAL
#include "esphome/components/captive_portal/captive_portal.h"
#endif
#ifdef USE_WEBSERVER_LOCAL
#if USE_WEBSERVER_VERSION == 2
#include "server_index_v2.h"
@@ -1957,9 +1961,19 @@ bool WebServer::canHandle(AsyncWebServerRequest *request) const {
const auto &url = request->url();
const auto method = request->method();
// Static URL checks
// Handle root URL
if (url == ESPHOME_F("/")) {
#ifdef USE_CAPTIVE_PORTAL
// When captive portal is active, only handle "/" if ?web_server param is present
if (captive_portal::global_captive_portal != nullptr && captive_portal::global_captive_portal->is_active()) {
return request->hasParam(ESPHOME_F("web_server"));
}
#endif
return true;
}
// Other static URL checks
static const char *const STATIC_URLS[] = {
"/",
#if !defined(USE_ESP32) && defined(USE_ARDUINO)
"/events",
#endif

View File

@@ -11,15 +11,18 @@ static const char *const TAG = "web_server_base";
WebServerBase *global_web_server_base = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
void WebServerBase::add_handler(AsyncWebHandler *handler) {
// remove all handlers
void WebServerBase::add_handler(AsyncWebHandler *handler, HandlerPosition position) {
#ifdef USE_WEBSERVER_AUTH
if (!credentials_.username.empty()) {
if (position != HandlerPosition::FALLBACK && !credentials_.username.empty()) {
handler = new internal::AuthMiddlewareHandler(handler, &credentials_);
}
#endif
this->handlers_.push_back(handler);
if (position == HandlerPosition::FALLBACK) {
this->handlers_.push_back(handler);
} else {
this->handlers_.insert(this->handlers_.begin() + this->fallback_start_, handler);
this->fallback_start_++;
}
if (this->server_ != nullptr) {
this->server_->addHandler(handler);
}

View File

@@ -91,6 +91,11 @@ class AuthMiddlewareHandler : public MiddlewareHandler {
} // namespace internal
enum class HandlerPosition : uint8_t {
NORMAL, ///< Before fallback handlers (default)
FALLBACK ///< After normal handlers (catch-all)
};
class WebServerBase : public Component {
public:
void init() {
@@ -122,7 +127,7 @@ class WebServerBase : public Component {
void set_auth_password(std::string auth_password) { credentials_.password = std::move(auth_password); }
#endif
void add_handler(AsyncWebHandler *handler);
void add_handler(AsyncWebHandler *handler, HandlerPosition position = HandlerPosition::NORMAL);
void set_port(uint16_t port) { port_ = port; }
uint16_t get_port() const { return port_; }
@@ -132,6 +137,7 @@ class WebServerBase : public Component {
uint16_t port_{80};
std::unique_ptr<AsyncWebServer> server_{nullptr};
std::vector<AsyncWebHandler *> handlers_;
size_t fallback_start_{0}; ///< Index where fallback handlers begin
#ifdef USE_WEBSERVER_AUTH
internal::Credentials credentials_;
#endif