Compare commits
14 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f3ec4b514d | ||
|
|
fc5798fa71 | ||
|
|
95d7ad543f | ||
|
|
d9b2903d78 | ||
|
|
32a664eedc | ||
|
|
e7477890cf | ||
|
|
9bf72ff05f | ||
|
|
46b4c970d1 | ||
|
|
c83ecf764d | ||
|
|
a2485a18cb | ||
|
|
8ef2ad17b5 | ||
|
|
4579f78bf9 | ||
|
|
1853407645 | ||
|
|
cb5efc1c42 |
@@ -121,7 +121,7 @@ async def to_code(config):
|
||||
decoded = base64.b64decode(conf[CONF_KEY])
|
||||
cg.add(var.set_noise_psk(list(decoded)))
|
||||
cg.add_define("USE_API_NOISE")
|
||||
cg.add_library("esphome/noise-c", "0.1.1")
|
||||
cg.add_library("esphome/noise-c", "0.1.3")
|
||||
else:
|
||||
cg.add_define("USE_API_PLAINTEXT")
|
||||
|
||||
|
||||
@@ -126,6 +126,14 @@ APIError APINoiseFrameHelper::init() {
|
||||
return APIError::TCP_NONBLOCKING_FAILED;
|
||||
}
|
||||
|
||||
int enable = 1;
|
||||
err = socket_->setsockopt(IPPROTO_TCP, TCP_NODELAY, &enable, sizeof(int));
|
||||
if (err != 0) {
|
||||
state_ = State::FAILED;
|
||||
HELPER_LOG("Setting nodelay failed with errno %d", errno);
|
||||
return APIError::TCP_NODELAY_FAILED;
|
||||
}
|
||||
|
||||
// init prologue
|
||||
prologue_.insert(prologue_.end(), PROLOGUE_INIT, PROLOGUE_INIT + strlen(PROLOGUE_INIT));
|
||||
|
||||
@@ -721,6 +729,13 @@ APIError APIPlaintextFrameHelper::init() {
|
||||
HELPER_LOG("Setting nonblocking failed with errno %d", errno);
|
||||
return APIError::TCP_NONBLOCKING_FAILED;
|
||||
}
|
||||
int enable = 1;
|
||||
err = socket_->setsockopt(IPPROTO_TCP, TCP_NODELAY, &enable, sizeof(int));
|
||||
if (err != 0) {
|
||||
state_ = State::FAILED;
|
||||
HELPER_LOG("Setting nodelay failed with errno %d", errno);
|
||||
return APIError::TCP_NODELAY_FAILED;
|
||||
}
|
||||
|
||||
state_ = State::DATA;
|
||||
return APIError::OK;
|
||||
|
||||
@@ -134,6 +134,11 @@ void APIServer::loop() {
|
||||
void APIServer::dump_config() {
|
||||
ESP_LOGCONFIG(TAG, "API Server:");
|
||||
ESP_LOGCONFIG(TAG, " Address: %s:%u", network_get_address().c_str(), this->port_);
|
||||
#ifdef USE_API_NOISE
|
||||
ESP_LOGCONFIG(TAG, " Using noise encryption: YES");
|
||||
#else
|
||||
ESP_LOGCONFIG(TAG, " Using noise encryption: NO");
|
||||
#endif
|
||||
}
|
||||
bool APIServer::uses_password() const { return !this->password_.empty(); }
|
||||
bool APIServer::check_password(const std::string &password) const {
|
||||
|
||||
@@ -281,4 +281,4 @@ async def to_code(config):
|
||||
if CONF_HUMIDITY_SETPOINT in config:
|
||||
sens = await sensor.new_sensor(config[CONF_HUMIDITY_SETPOINT])
|
||||
cg.add(var.set_humidity_setpoint_sensor(sens))
|
||||
cg.add_library("dudanov/MideaUART", "1.1.5")
|
||||
cg.add_library("dudanov/MideaUART", "1.1.8")
|
||||
|
||||
@@ -96,6 +96,9 @@ class BSDSocketImpl : public Socket {
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
#elif defined(ARDUINO_ARCH_ESP32)
|
||||
// ESP-IDF v4 only has symbol lwip_readv
|
||||
return ::lwip_readv(fd_, iov, iovcnt);
|
||||
#else
|
||||
return ::readv(fd_, iov, iovcnt);
|
||||
#endif
|
||||
@@ -120,6 +123,9 @@ class BSDSocketImpl : public Socket {
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
#elif defined(ARDUINO_ARCH_ESP32)
|
||||
// ESP-IDF v4 only has symbol lwip_writev
|
||||
return ::lwip_writev(fd_, iov, iovcnt);
|
||||
#else
|
||||
return ::writev(fd_, iov, iovcnt);
|
||||
#endif
|
||||
|
||||
@@ -256,7 +256,7 @@ class LWIPRawImpl : public Socket {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
*reinterpret_cast<int *>(optval) = tcp_nagle_disabled(pcb_);
|
||||
*reinterpret_cast<int *>(optval) = nodelay_;
|
||||
*optlen = 4;
|
||||
return 0;
|
||||
}
|
||||
@@ -285,11 +285,7 @@ class LWIPRawImpl : public Socket {
|
||||
return -1;
|
||||
}
|
||||
int val = *reinterpret_cast<const int *>(optval);
|
||||
if (val != 0) {
|
||||
tcp_nagle_disable(pcb_);
|
||||
} else {
|
||||
tcp_nagle_enable(pcb_);
|
||||
}
|
||||
nodelay_ = val;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -443,9 +439,11 @@ class LWIPRawImpl : public Socket {
|
||||
if (written == 0)
|
||||
// no need to output if nothing written
|
||||
return 0;
|
||||
int err = internal_output();
|
||||
if (err == -1)
|
||||
return -1;
|
||||
if (nodelay_) {
|
||||
int err = internal_output();
|
||||
if (err == -1)
|
||||
return -1;
|
||||
}
|
||||
return written;
|
||||
}
|
||||
ssize_t writev(const struct iovec *iov, int iovcnt) override {
|
||||
@@ -465,9 +463,11 @@ class LWIPRawImpl : public Socket {
|
||||
if (written == 0)
|
||||
// no need to output if nothing written
|
||||
return 0;
|
||||
int err = internal_output();
|
||||
if (err == -1)
|
||||
return -1;
|
||||
if (nodelay_) {
|
||||
int err = internal_output();
|
||||
if (err == -1)
|
||||
return -1;
|
||||
}
|
||||
return written;
|
||||
}
|
||||
int setblocking(bool blocking) override {
|
||||
@@ -549,6 +549,9 @@ class LWIPRawImpl : public Socket {
|
||||
bool rx_closed_ = false;
|
||||
pbuf *rx_buf_ = nullptr;
|
||||
size_t rx_buf_offset_ = 0;
|
||||
// don't use lwip nodelay flag, it sometimes causes reconnect
|
||||
// instead use it for determining whether to call lwip_output
|
||||
bool nodelay_ = false;
|
||||
};
|
||||
|
||||
std::unique_ptr<Socket> socket(int domain, int type, int protocol) {
|
||||
|
||||
@@ -34,8 +34,8 @@ CONFIG_SCHEMA = cv.Schema(
|
||||
cv.Optional(CONF_JS_INCLUDE): cv.file_,
|
||||
cv.Optional(CONF_AUTH): cv.Schema(
|
||||
{
|
||||
cv.Required(CONF_USERNAME): cv.string_strict,
|
||||
cv.Required(CONF_PASSWORD): cv.string_strict,
|
||||
cv.Required(CONF_USERNAME): cv.All(cv.string_strict, cv.Length(min=1)),
|
||||
cv.Required(CONF_PASSWORD): cv.All(cv.string_strict, cv.Length(min=1)),
|
||||
}
|
||||
),
|
||||
cv.GenerateID(CONF_WEB_SERVER_BASE_ID): cv.use_id(
|
||||
@@ -57,8 +57,8 @@ async def to_code(config):
|
||||
cg.add(var.set_css_url(config[CONF_CSS_URL]))
|
||||
cg.add(var.set_js_url(config[CONF_JS_URL]))
|
||||
if CONF_AUTH in config:
|
||||
cg.add(var.set_username(config[CONF_AUTH][CONF_USERNAME]))
|
||||
cg.add(var.set_password(config[CONF_AUTH][CONF_PASSWORD]))
|
||||
cg.add(paren.set_auth_username(config[CONF_AUTH][CONF_USERNAME]))
|
||||
cg.add(paren.set_auth_password(config[CONF_AUTH][CONF_PASSWORD]))
|
||||
if CONF_CSS_INCLUDE in config:
|
||||
cg.add_define("WEBSERVER_CSS_INCLUDE")
|
||||
path = CORE.relative_config_path(config[CONF_CSS_INCLUDE])
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
#include "web_server.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include "esphome/core/application.h"
|
||||
#include "esphome/core/util.h"
|
||||
#include "esphome/components/json/json_util.h"
|
||||
#include "esphome/core/application.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include "esphome/core/util.h"
|
||||
|
||||
#include "StreamString.h"
|
||||
|
||||
@@ -151,9 +151,6 @@ void WebServer::setup() {
|
||||
void WebServer::dump_config() {
|
||||
ESP_LOGCONFIG(TAG, "Web Server:");
|
||||
ESP_LOGCONFIG(TAG, " Address: %s:%u", network_get_address().c_str(), this->base_->get_port());
|
||||
if (this->using_auth()) {
|
||||
ESP_LOGCONFIG(TAG, " Basic authentication enabled");
|
||||
}
|
||||
}
|
||||
float WebServer::get_setup_priority() const { return setup_priority::WIFI - 1.0f; }
|
||||
|
||||
@@ -728,10 +725,6 @@ bool WebServer::canHandle(AsyncWebServerRequest *request) {
|
||||
return false;
|
||||
}
|
||||
void WebServer::handleRequest(AsyncWebServerRequest *request) {
|
||||
if (this->using_auth() && !request->authenticate(this->username_, this->password_)) {
|
||||
return request->requestAuthentication();
|
||||
}
|
||||
|
||||
if (request->url() == "/") {
|
||||
this->handle_index_request(request);
|
||||
return;
|
||||
|
||||
@@ -30,10 +30,6 @@ class WebServer : public Controller, public Component, public AsyncWebHandler {
|
||||
public:
|
||||
WebServer(web_server_base::WebServerBase *base) : base_(base) {}
|
||||
|
||||
void set_username(const char *username) { username_ = username; }
|
||||
|
||||
void set_password(const char *password) { password_ = password; }
|
||||
|
||||
/** Set the URL to the CSS <link> that's sent to each client. Defaults to
|
||||
* https://esphome.io/_static/webserver-v1.min.css
|
||||
*
|
||||
@@ -83,8 +79,6 @@ class WebServer : public Controller, public Component, public AsyncWebHandler {
|
||||
void handle_js_request(AsyncWebServerRequest *request);
|
||||
#endif
|
||||
|
||||
bool using_auth() { return username_ != nullptr && password_ != nullptr; }
|
||||
|
||||
#ifdef USE_SENSOR
|
||||
void on_sensor_update(sensor::Sensor *obj, float state) override;
|
||||
/// Handle a sensor request under '/sensor/<id>'.
|
||||
@@ -182,8 +176,6 @@ class WebServer : public Controller, public Component, public AsyncWebHandler {
|
||||
protected:
|
||||
web_server_base::WebServerBase *base_;
|
||||
AsyncEventSource events_{"/events"};
|
||||
const char *username_{nullptr};
|
||||
const char *password_{nullptr};
|
||||
const char *css_url_{nullptr};
|
||||
const char *css_include_{nullptr};
|
||||
const char *js_url_{nullptr};
|
||||
|
||||
@@ -15,6 +15,17 @@ namespace web_server_base {
|
||||
|
||||
static const char *const TAG = "web_server_base";
|
||||
|
||||
void WebServerBase::add_handler(AsyncWebHandler *handler) {
|
||||
// remove all handlers
|
||||
|
||||
if (!credentials_.username.empty()) {
|
||||
handler = new internal::AuthMiddlewareHandler(handler, &credentials_);
|
||||
}
|
||||
this->handlers_.push_back(handler);
|
||||
if (this->server_ != nullptr)
|
||||
this->server_->addHandler(handler);
|
||||
}
|
||||
|
||||
void report_ota_error() {
|
||||
StreamString ss;
|
||||
Update.printError(ss);
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
#include "esphome/core/component.h"
|
||||
|
||||
#include <ESPAsyncWebServer.h>
|
||||
@@ -7,6 +9,68 @@
|
||||
namespace esphome {
|
||||
namespace web_server_base {
|
||||
|
||||
namespace internal {
|
||||
|
||||
class MiddlewareHandler : public AsyncWebHandler {
|
||||
public:
|
||||
MiddlewareHandler(AsyncWebHandler *next) : next_(next) {}
|
||||
|
||||
bool canHandle(AsyncWebServerRequest *request) override { return next_->canHandle(request); }
|
||||
void handleRequest(AsyncWebServerRequest *request) override { next_->handleRequest(request); }
|
||||
void handleUpload(AsyncWebServerRequest *request, const String &filename, size_t index, uint8_t *data, size_t len,
|
||||
bool final) override {
|
||||
next_->handleUpload(request, filename, index, data, len, final);
|
||||
}
|
||||
void handleBody(AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total) override {
|
||||
next_->handleBody(request, data, len, index, total);
|
||||
}
|
||||
bool isRequestHandlerTrivial() override { return next_->isRequestHandlerTrivial(); }
|
||||
|
||||
protected:
|
||||
AsyncWebHandler *next_;
|
||||
};
|
||||
|
||||
struct Credentials {
|
||||
std::string username;
|
||||
std::string password;
|
||||
};
|
||||
|
||||
class AuthMiddlewareHandler : public MiddlewareHandler {
|
||||
public:
|
||||
AuthMiddlewareHandler(AsyncWebHandler *next, Credentials *credentials)
|
||||
: MiddlewareHandler(next), credentials_(credentials) {}
|
||||
|
||||
bool check_auth(AsyncWebServerRequest *request) {
|
||||
bool success = request->authenticate(credentials_->username.c_str(), credentials_->password.c_str());
|
||||
if (!success) {
|
||||
request->requestAuthentication();
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
void handleRequest(AsyncWebServerRequest *request) override {
|
||||
if (!check_auth(request))
|
||||
return;
|
||||
MiddlewareHandler::handleRequest(request);
|
||||
}
|
||||
void handleUpload(AsyncWebServerRequest *request, const String &filename, size_t index, uint8_t *data, size_t len,
|
||||
bool final) override {
|
||||
if (!check_auth(request))
|
||||
return;
|
||||
MiddlewareHandler::handleUpload(request, filename, index, data, len, final);
|
||||
}
|
||||
void handleBody(AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total) override {
|
||||
if (!check_auth(request))
|
||||
return;
|
||||
MiddlewareHandler::handleBody(request, data, len, index, total);
|
||||
}
|
||||
|
||||
protected:
|
||||
Credentials *credentials_;
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
|
||||
class WebServerBase : public Component {
|
||||
public:
|
||||
void init() {
|
||||
@@ -32,13 +96,10 @@ class WebServerBase : public Component {
|
||||
AsyncWebServer *get_server() const { return server_; }
|
||||
float get_setup_priority() const override;
|
||||
|
||||
void add_handler(AsyncWebHandler *handler) {
|
||||
// remove all handlers
|
||||
void set_auth_username(std::string auth_username) { credentials_.username = std::move(auth_username); }
|
||||
void set_auth_password(std::string auth_password) { credentials_.password = std::move(auth_password); }
|
||||
|
||||
this->handlers_.push_back(handler);
|
||||
if (this->server_ != nullptr)
|
||||
this->server_->addHandler(handler);
|
||||
}
|
||||
void add_handler(AsyncWebHandler *handler);
|
||||
|
||||
void add_ota_handler();
|
||||
|
||||
@@ -52,6 +113,7 @@ class WebServerBase : public Component {
|
||||
uint16_t port_{80};
|
||||
AsyncWebServer *server_{nullptr};
|
||||
std::vector<AsyncWebHandler *> handlers_;
|
||||
internal::Credentials credentials_;
|
||||
};
|
||||
|
||||
class OTARequestHandler : public AsyncWebHandler {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"""Constants used by esphome."""
|
||||
|
||||
__version__ = "2021.9.1"
|
||||
__version__ = "2021.9.3"
|
||||
|
||||
ESP_PLATFORM_ESP32 = "ESP32"
|
||||
ESP_PLATFORM_ESP8266 = "ESP8266"
|
||||
|
||||
@@ -36,8 +36,8 @@ lib_deps =
|
||||
6306@1.0.3 ; HM3301
|
||||
glmnet/Dsmr@0.3 ; used by dsmr
|
||||
rweather/Crypto@0.2.0 ; used by dsmr
|
||||
esphome/noise-c@0.1.1 ; used by api
|
||||
dudanov/MideaUART@1.1.0 ; used by midea
|
||||
esphome/noise-c@0.1.3 ; used by api
|
||||
dudanov/MideaUART@1.1.8 ; used by midea
|
||||
|
||||
build_flags =
|
||||
-DESPHOME_LOG_LEVEL=ESPHOME_LOG_LEVEL_VERY_VERBOSE
|
||||
|
||||
@@ -10,4 +10,4 @@ platformio==5.2.0
|
||||
esptool==3.1
|
||||
click==7.1.2
|
||||
esphome-dashboard==20210908.0
|
||||
aioesphomeapi==9.0.0
|
||||
aioesphomeapi==9.1.4
|
||||
|
||||
Reference in New Issue
Block a user