8 #include "esp_tls_crypto.h" 14 namespace web_server_idf {
17 #define HTTPD_409 "409 Conflict" 20 #define CRLF_STR "\r\n" 21 #define CRLF_LEN (sizeof(CRLF_STR) - 1) 23 static const char *
const TAG =
"web_server_idf";
36 httpd_config_t config = HTTPD_DEFAULT_CONFIG();
37 config.server_port = this->
port_;
38 config.uri_match_fn = [](
const char * ,
const char * ,
size_t ) {
return true; };
39 if (httpd_start(&this->
server_, &config) == ESP_OK) {
40 const httpd_uri_t handler_get = {
46 httpd_register_uri_handler(this->
server_, &handler_get);
48 const httpd_uri_t handler_post = {
54 httpd_register_uri_handler(this->
server_, &handler_post);
56 const httpd_uri_t handler_options = {
58 .method = HTTP_OPTIONS,
62 httpd_register_uri_handler(this->
server_, &handler_options);
67 ESP_LOGVV(TAG,
"Enter AsyncWebServer::request_post_handler. uri=%s", r->uri);
69 if (content_type.has_value() && *content_type !=
"application/x-www-form-urlencoded") {
70 ESP_LOGW(TAG,
"Only application/x-www-form-urlencoded supported for POST request");
76 ESP_LOGW(TAG,
"Content length is requred for post: %s", r->uri);
77 httpd_resp_send_err(r, HTTPD_411_LENGTH_REQUIRED,
nullptr);
81 if (r->content_len > HTTPD_MAX_REQ_HDR_LEN) {
82 ESP_LOGW(TAG,
"Request size is to big: %zu", r->content_len);
83 httpd_resp_send_err(r, HTTPD_400_BAD_REQUEST,
nullptr);
87 std::string post_query;
88 if (r->content_len > 0) {
89 post_query.resize(r->content_len);
90 const int ret = httpd_req_recv(r, &post_query[0], r->content_len + 1);
92 if (ret == HTTPD_SOCK_ERR_TIMEOUT) {
93 httpd_resp_send_err(r, HTTPD_408_REQ_TIMEOUT,
nullptr);
94 return ESP_ERR_TIMEOUT;
96 httpd_resp_send_err(r, HTTPD_400_BAD_REQUEST,
nullptr);
106 ESP_LOGVV(TAG,
"Enter AsyncWebServer::request_handler. method=%u, uri=%s", r->method, r->uri);
113 if (handler->canHandle(request)) {
116 handler->handleRequest(request);
124 return ESP_ERR_NOT_FOUND;
129 for (
const auto &pair : this->params_) {
141 auto *str = strchr(this->req_->uri,
'?');
142 if (str ==
nullptr) {
143 return this->req_->uri;
145 return std::string(this->req_->uri, str - this->req_->uri);
155 this->init_response_(
nullptr, code, content_type);
157 httpd_resp_send(*
this, content, HTTPD_RESP_USE_STRLEN);
159 httpd_resp_send(*
this,
nullptr, 0);
164 httpd_resp_set_status(*
this,
"302 Found");
165 httpd_resp_set_hdr(*
this,
"Location", url.c_str());
166 httpd_resp_send(*
this,
nullptr, 0);
170 httpd_resp_set_status(*
this, code == 200 ? HTTPD_200
171 : code == 404 ? HTTPD_404
172 : code == 409 ? HTTPD_409
175 if (content_type && *content_type) {
176 httpd_resp_set_type(*
this, content_type);
178 httpd_resp_set_hdr(*
this,
"Accept-Ranges",
"none");
181 httpd_resp_set_hdr(*
this, pair.first.c_str(), pair.second.c_str());
189 if (username ==
nullptr || password ==
nullptr || *username == 0) {
192 auto auth = this->get_header(
"Authorization");
193 if (!auth.has_value()) {
197 auto *auth_str = auth.value().c_str();
199 const auto auth_prefix_len =
sizeof(
"Basic ") - 1;
200 if (strncmp(
"Basic ", auth_str, auth_prefix_len) != 0) {
201 ESP_LOGW(TAG,
"Only Basic authorization supported yet");
205 std::string user_info;
206 user_info += username;
208 user_info += password;
211 esp_crypto_base64_encode(
nullptr, 0, &n, reinterpret_cast<const uint8_t *>(user_info.c_str()), user_info.size());
213 auto digest = std::unique_ptr<char[]>(
new char[n + 1]);
214 esp_crypto_base64_encode(reinterpret_cast<uint8_t *>(digest.get()), n, &out,
215 reinterpret_cast<const uint8_t *>(user_info.c_str()), user_info.size());
217 return strncmp(digest.get(), auth_str + auth_prefix_len, auth.value().size() - auth_prefix_len) == 0;
221 httpd_resp_set_hdr(*
this,
"Connection",
"keep-alive");
222 auto auth_val =
str_sprintf(
"Basic realm=\"%s\"", realm ? realm :
"Login Required");
223 httpd_resp_set_hdr(*
this,
"WWW-Authenticate", auth_val.c_str());
224 httpd_resp_send_err(*
this, HTTPD_401_UNAUTHORIZED,
nullptr);
228 auto find = this->params_.find(name);
229 if (find != this->params_.end()) {
236 if (url_query.has_value()) {
245 this->params_.insert({
name, param});
250 httpd_resp_set_hdr(*this->req_, name, value);
259 const int length = vsnprintf(
nullptr, 0, fmt, args);
266 vsnprintf(&str[0], length + 1, fmt, args);
273 for (
auto *ses : this->sessions_) {
280 if (this->on_connect_) {
281 this->on_connect_(rsp);
283 this->sessions_.insert(rsp);
287 for (
auto *ses : this->sessions_) {
288 ses->send(message, event,
id, reconnect);
294 httpd_req_t *req = *request;
296 httpd_resp_set_status(req, HTTPD_200);
297 httpd_resp_set_type(req,
"text/event-stream");
298 httpd_resp_set_hdr(req,
"Cache-Control",
"no-cache");
299 httpd_resp_set_hdr(req,
"Connection",
"keep-alive");
302 httpd_resp_set_hdr(req, pair.first.c_str(), pair.second.c_str());
305 httpd_resp_send_chunk(req, CRLF_STR, CRLF_LEN);
307 req->sess_ctx =
this;
310 this->
hd_ = req->handle;
311 this->
fd_ = httpd_req_to_sockfd(req);
321 if (this->
fd_ == 0) {
328 ev.append(
"retry: ",
sizeof(
"retry: ") - 1);
330 ev.append(CRLF_STR, CRLF_LEN);
334 ev.append(
"id: ",
sizeof(
"id: ") - 1);
336 ev.append(CRLF_STR, CRLF_LEN);
339 if (event && *event) {
340 ev.append(
"event: ",
sizeof(
"event: ") - 1);
342 ev.append(CRLF_STR, CRLF_LEN);
345 if (message && *message) {
346 ev.append(
"data: ",
sizeof(
"data: ") - 1);
348 ev.append(CRLF_STR, CRLF_LEN);
355 ev.append(CRLF_STR, CRLF_LEN);
358 auto cs =
str_snprintf(
"%x" CRLF_STR, 4 *
sizeof(ev.size()) + CRLF_LEN, ev.size());
359 httpd_socket_send(this->
hd_, this->
fd_, cs.c_str(), cs.size(), 0);
362 httpd_socket_send(this->
hd_, this->
fd_, ev.c_str(), ev.size(), 0);
365 httpd_socket_send(this->
hd_, this->
fd_, CRLF_STR, CRLF_LEN, 0);
371 #endif // !defined(USE_ESP_IDF)
value_type const & value() const
AsyncWebParameter * getParam(const std::string &name)
void send(AsyncWebServerResponse *response)
bool hasHeader(const char *name) const
optional< std::string > get_header(const char *name) const
static esp_err_t request_handler(httpd_req_t *r)
AsyncEventSourceResponse(const AsyncWebServerRequest *request, AsyncEventSource *server)
virtual size_t get_content_size() const =0
void send(const char *message, const char *event=nullptr, uint32_t id=0, uint32_t reconnect=0)
optional< std::string > request_get_header(httpd_req_t *req, const char *name)
~AsyncEventSource() override
void print(const char *str)
bool request_has_header(httpd_req_t *req, const char *name)
AsyncEventSource * server_
void init_response_(AsyncWebServerResponse *rsp, int code, const char *content_type)
std::vector< AsyncWebHandler * > handlers_
void send(const char *message, const char *event=nullptr, uint32_t id=0, uint32_t reconnect=0)
std::string str_sprintf(const char *fmt,...)
optional< std::string > query_key_value(const std::string &query_url, const std::string &key)
void handleRequest(AsyncWebServerRequest *request) override
optional< std::string > request_get_url_query(httpd_req_t *req)
void addHeader(const char *name, const char *value)
esp_err_t request_handler_(AsyncWebServerRequest *request) const
void requestAuthentication(const char *realm=nullptr) const
bool authenticate(const char *username, const char *password) const
std::string to_string(int value)
void redirect(const std::string &url)
Implementation of SPI Controller mode.
std::set< AsyncEventSourceResponse * > sessions_
std::string str_snprintf(const char *fmt, size_t len,...)
std::function< void(AsyncWebServerRequest *request)> on_not_found_
virtual const char * get_content_data() const =0
static esp_err_t request_post_handler(httpd_req_t *r)
static void destroy(void *p)
void printf(const char *fmt,...) __attribute__((format(printf