8#include "esp_tls_crypto.h"
20namespace web_server_idf {
23#define HTTPD_409 "409 Conflict"
26#define CRLF_STR "\r\n"
27#define CRLF_LEN (sizeof(CRLF_STR) - 1)
29static const char *
const TAG =
"web_server_idf";
42 httpd_config_t config = HTTPD_DEFAULT_CONFIG();
43 config.server_port = this->
port_;
44 config.uri_match_fn = [](
const char * ,
const char * ,
size_t ) {
return true; };
45 if (httpd_start(&this->
server_, &config) == ESP_OK) {
46 const httpd_uri_t handler_get = {
52 httpd_register_uri_handler(this->
server_, &handler_get);
54 const httpd_uri_t handler_post = {
60 httpd_register_uri_handler(this->
server_, &handler_post);
62 const httpd_uri_t handler_options = {
64 .method = HTTP_OPTIONS,
68 httpd_register_uri_handler(this->
server_, &handler_options);
73 ESP_LOGVV(TAG,
"Enter AsyncWebServer::request_post_handler. uri=%s", r->uri);
75 if (content_type.has_value() && *content_type !=
"application/x-www-form-urlencoded") {
76 ESP_LOGW(TAG,
"Only application/x-www-form-urlencoded supported for POST request");
82 ESP_LOGW(TAG,
"Content length is requred for post: %s", r->uri);
83 httpd_resp_send_err(r, HTTPD_411_LENGTH_REQUIRED,
nullptr);
87 if (r->content_len > HTTPD_MAX_REQ_HDR_LEN) {
88 ESP_LOGW(TAG,
"Request size is to big: %zu", r->content_len);
89 httpd_resp_send_err(r, HTTPD_400_BAD_REQUEST,
nullptr);
93 std::string post_query;
94 if (r->content_len > 0) {
95 post_query.resize(r->content_len);
96 const int ret = httpd_req_recv(r, &post_query[0], r->content_len + 1);
98 if (ret == HTTPD_SOCK_ERR_TIMEOUT) {
99 httpd_resp_send_err(r, HTTPD_408_REQ_TIMEOUT,
nullptr);
100 return ESP_ERR_TIMEOUT;
102 httpd_resp_send_err(r, HTTPD_400_BAD_REQUEST,
nullptr);
112 ESP_LOGVV(TAG,
"Enter AsyncWebServer::request_handler. method=%u, uri=%s", r->method, r->uri);
119 if (handler->canHandle(request)) {
122 handler->handleRequest(request);
130 return ESP_ERR_NOT_FOUND;
135 for (
const auto &pair : this->
params_) {
147 auto *str = strchr(this->
req_->uri,
'?');
148 if (str ==
nullptr) {
149 return this->
req_->uri;
151 return std::string(this->
req_->uri, str - this->req_->uri);
163 httpd_resp_send(*
this, content, HTTPD_RESP_USE_STRLEN);
165 httpd_resp_send(*
this,
nullptr, 0);
170 httpd_resp_set_status(*
this,
"302 Found");
171 httpd_resp_set_hdr(*
this,
"Location",
url.c_str());
172 httpd_resp_send(*
this,
nullptr, 0);
176 httpd_resp_set_status(*
this, code == 200 ? HTTPD_200
177 : code == 404 ? HTTPD_404
178 : code == 409 ? HTTPD_409
181 if (content_type && *content_type) {
182 httpd_resp_set_type(*
this, content_type);
184 httpd_resp_set_hdr(*
this,
"Accept-Ranges",
"none");
187 httpd_resp_set_hdr(*
this, pair.first.c_str(), pair.second.c_str());
195 if (username ==
nullptr || password ==
nullptr || *username == 0) {
198 auto auth = this->
get_header(
"Authorization");
199 if (!auth.has_value()) {
203 auto *auth_str = auth.value().c_str();
205 const auto auth_prefix_len =
sizeof(
"Basic ") - 1;
206 if (strncmp(
"Basic ", auth_str, auth_prefix_len) != 0) {
207 ESP_LOGW(TAG,
"Only Basic authorization supported yet");
211 std::string user_info;
212 user_info += username;
214 user_info += password;
217 esp_crypto_base64_encode(
nullptr, 0, &n,
reinterpret_cast<const uint8_t *
>(user_info.c_str()), user_info.size());
219 auto digest = std::unique_ptr<char[]>(
new char[n + 1]);
220 esp_crypto_base64_encode(
reinterpret_cast<uint8_t *
>(digest.get()), n, &out,
221 reinterpret_cast<const uint8_t *
>(user_info.c_str()), user_info.size());
223 return strncmp(digest.get(), auth_str + auth_prefix_len, auth.value().size() - auth_prefix_len) == 0;
227 httpd_resp_set_hdr(*
this,
"Connection",
"keep-alive");
228 auto auth_val =
str_sprintf(
"Basic realm=\"%s\"", realm ? realm :
"Login Required");
229 httpd_resp_set_hdr(*
this,
"WWW-Authenticate", auth_val.c_str());
230 httpd_resp_send_err(*
this, HTTPD_401_UNAUTHORIZED,
nullptr);
234 auto find = this->
params_.find(name);
235 if (find != this->
params_.end()) {
240 if (!
val.has_value()) {
242 if (url_query.has_value()) {
248 if (
val.has_value()) {
251 this->
params_.insert({name, param});
256 httpd_resp_set_hdr(*this->
req_, name, value);
265 const int length = vsnprintf(
nullptr, 0, fmt, args);
272 vsnprintf(&str[0],
length + 1, fmt, args);
302 ses->try_send_nodefer(message, event,
id, reconnect);
309 ses->deferrable_send_state(source, event_type, message_generator);
316 : server_(server), web_server_(ws), entities_iterator_(new
esphome::web_server::ListEntitiesIterator(ws, server)) {
317 httpd_req_t *req = *request;
319 httpd_resp_set_status(req, HTTPD_200);
320 httpd_resp_set_type(req,
"text/event-stream");
321 httpd_resp_set_hdr(req,
"Cache-Control",
"no-cache");
322 httpd_resp_set_hdr(req,
"Connection",
"keep-alive");
325 httpd_resp_set_hdr(req, pair.first.c_str(), pair.second.c_str());
328 httpd_resp_send_chunk(req, CRLF_STR, CRLF_LEN);
330 req->sess_ctx =
this;
333 this->
hd_ = req->handle;
334 this->
fd_ = httpd_req_to_sockfd(req);
343 root[
"name"] = group.second.name;
344 root[
"sorting_weight"] = group.second.weight;
371 auto iter = std::find_if(this->
deferred_queue_.begin(), this->deferred_queue_.end(),
372 [&item](
const DeferredEvent &test) ->
bool { return test == item; });
406 if (bytes_sent == HTTPD_SOCK_ERR_TIMEOUT || bytes_sent == HTTPD_SOCK_ERR_FAIL) {
425 uint32_t reconnect) {
426 if (this->
fd_ == 0) {
437 const char chunk_len_header[] =
" " CRLF_STR;
438 const int chunk_len_header_len =
sizeof(chunk_len_header) - 1;
454 if (event && *event) {
460 if (message && *message) {
474 int chunk_len =
event_buffer_.size() - CRLF_LEN - chunk_len_header_len;
475 char chunk_len_str[9];
476 snprintf(chunk_len_str, 9,
"%08x", chunk_len);
492 if (source ==
nullptr)
494 if (event_type ==
nullptr)
496 if (message_generator ==
nullptr)
499 if (0 != strcmp(event_type,
"state_detail_all") && 0 != strcmp(event_type,
"state")) {
500 ESP_LOGE(TAG,
"Can't defer non-state event");
511 std::string message = message_generator(
web_server_, source);
void begin(bool include_internal=false)
value_type const & value() const
This class allows users to create a web server with their ESP nodes.
std::string get_config_json()
Return the webserver configuration as JSON.
std::map< uint64_t, SortingGroup > sorting_groups_
~AsyncEventSource() override
friend class AsyncEventSourceResponse
std::set< AsyncEventSourceResponse * > sessions_
void deferrable_send_state(void *source, const char *event_type, message_generator_t *message_generator)
esphome::web_server::WebServer * web_server_
void try_send_nodefer(const char *message, const char *event=nullptr, uint32_t id=0, uint32_t reconnect=0)
void handleRequest(AsyncWebServerRequest *request) override
connect_handler_t on_connect_
static void destroy(void *p)
std::vector< DeferredEvent > deferred_queue_
void deferrable_send_state(void *source, const char *event_type, message_generator_t *message_generator)
esphome::web_server::WebServer * web_server_
void deq_push_back_with_dedup_(void *source, message_generator_t *message_generator)
AsyncEventSource * server_
void process_deferred_queue_()
AsyncEventSourceResponse(const AsyncWebServerRequest *request, esphome::web_server_idf::AsyncEventSource *server, esphome::web_server::WebServer *ws)
std::unique_ptr< esphome::web_server::ListEntitiesIterator > entities_iterator_
bool try_send_nodefer(const char *message, const char *event=nullptr, uint32_t id=0, uint32_t reconnect=0)
std::string event_buffer_
void print(const char *str)
void printf(const char *fmt,...) __attribute__((format(printf
std::function< void(AsyncWebServerRequest *request)> on_not_found_
static esp_err_t request_post_handler(httpd_req_t *r)
std::vector< AsyncWebHandler * > handlers_
esp_err_t request_handler_(AsyncWebServerRequest *request) const
static esp_err_t request_handler(httpd_req_t *r)
AsyncWebParameter * getParam(const std::string &name)
optional< std::string > get_header(const char *name) const
void send(AsyncWebServerResponse *response)
std::map< std::string, AsyncWebParameter * > params_
bool hasHeader(const char *name) const
void init_response_(AsyncWebServerResponse *rsp, int code, const char *content_type)
void requestAuthentication(const char *realm=nullptr) const
AsyncWebServerResponse * rsp_
bool authenticate(const char *username, const char *password) const
void redirect(const std::string &url)
const AsyncWebServerRequest * req_
virtual const char * get_content_data() const =0
virtual size_t get_content_size() const =0
void addHeader(const char *name, const char *value)
std::string build_json(const json_build_t &f)
Build a JSON string with the provided json build function.
optional< std::string > request_get_url_query(httpd_req_t *req)
optional< std::string > request_get_header(httpd_req_t *req, const char *name)
std::string(esphome::web_server::WebServer *, void *) message_generator_t
optional< std::string > query_key_value(const std::string &query_url, const std::string &key)
bool request_has_header(httpd_req_t *req, const char *name)
Providing packet encoding functions for exchanging data with a remote host.
std::string to_string(int value)
std::string str_sprintf(const char *fmt,...)
uint32_t IRAM_ATTR HOT millis()
message_generator_t * message_generator_