diff --git a/components/esp_http_server/include/esp_http_server.h b/components/esp_http_server/include/esp_http_server.h index 519538f9a..793f3dd80 100644 --- a/components/esp_http_server/include/esp_http_server.h +++ b/components/esp_http_server/include/esp_http_server.h @@ -150,6 +150,10 @@ typedef struct httpd_config { * function for freeing the global user context, please specify that here. */ void * global_user_ctx; + + /** + * Free function for global user context + */ httpd_free_ctx_fn_t global_user_ctx_free_fn; /** @@ -159,6 +163,10 @@ typedef struct httpd_config { * It will be freed using free(), unless global_transport_ctx_free_fn is specified. */ void * global_transport_ctx; + + /** + * Free function for global transport context + */ httpd_free_ctx_fn_t global_transport_ctx_free_fn; /** @@ -509,100 +517,64 @@ typedef int (*httpd_pending_func_t)(httpd_handle_t hd, int sockfd); */ /** - * @brief Override web server's receive function + * @brief Override web server's receive function (by session FD) * * This function overrides the web server's receive function. This same function is - * used to read and parse HTTP headers as well as body. + * used to read HTTP request packets. * - * @note This API is supposed to be called only from the context of - * a URI handler where httpd_req_t* request pointer is valid. + * @note This API is supposed to be called either from the context of + * - an http session APIs where sockfd is a valid parameter + * - a URI handler where sockfd is obtained using httpd_req_to_sockfd() * - * @param[in] r The request being responded to - * @param[in] recv_func The receive function to be set for this request + * @param[in] hd HTTPD instance handle + * @param[in] sockfd Session socket FD + * @param[in] recv_func The receive function to be set for this session * * @return * - ESP_OK : On successfully registering override * - ESP_ERR_INVALID_ARG : Null arguments - * - ESP_ERR_HTTPD_INVALID_REQ : Invalid request pointer */ -esp_err_t httpd_set_recv_override(httpd_req_t *r, httpd_recv_func_t recv_func); - -/** - * @brief Override web server's send function - * - * This function overrides the web server's send function. This same function is - * used to send out any response to any HTTP request. - * - * @note This API is supposed to be called only from the context of - * a URI handler where httpd_req_t* request pointer is valid. - * - * @param[in] r The request being responded to - * @param[in] send_func The send function to be set for this request - * - * @return - * - ESP_OK : On successfully registering override - * - ESP_ERR_INVALID_ARG : Null arguments - * - ESP_ERR_HTTPD_INVALID_REQ : Invalid request pointer - */ -esp_err_t httpd_set_send_override(httpd_req_t *r, httpd_send_func_t send_func); - -/** - * @brief Override web server's pending function - * - * This function overrides the web server's pending function. This function is - * used to test for pending bytes in a socket. - * - * @note This API is supposed to be called only from the context of - * a URI handler where httpd_req_t* request pointer is valid. - * - * @param[in] r The request being responded to - * @param[in] pending_func The pending function to be set for this request - * - * @return - * - ESP_OK : On successfully registering override - * - ESP_ERR_INVALID_ARG : Null arguments - * - ESP_ERR_HTTPD_INVALID_REQ : Invalid request pointer - */ -esp_err_t httpd_set_pending_override(httpd_req_t *r, httpd_pending_func_t pending_func); +esp_err_t httpd_sess_set_recv_override(httpd_handle_t hd, int sockfd, httpd_recv_func_t recv_func); /** * @brief Override web server's send function (by session FD) * - * @see httpd_set_send_override() + * This function overrides the web server's send function. This same function is + * used to send out any response to any HTTP request. * - * @param[in] hd HTTPD instance handle - * @param[in] fd session socket FD + * @note This API is supposed to be called either from the context of + * - an http session APIs where sockfd is a valid parameter + * - a URI handler where sockfd is obtained using httpd_req_to_sockfd() + * + * @param[in] hd HTTPD instance handle + * @param[in] sockfd Session socket FD * @param[in] send_func The send function to be set for this session * - * @return status code + * @return + * - ESP_OK : On successfully registering override + * - ESP_ERR_INVALID_ARG : Null arguments */ -esp_err_t httpd_set_sess_send_override(httpd_handle_t hd, int sockfd, httpd_send_func_t send_func); - -/** - * @brief Override web server's receive function (by session FD) - * - * @see httpd_set_recv_override() - * - * @param[in] hd HTTPD instance handle - * @param[in] fd session socket FD - * @param[in] recv_func The receive function to be set for this session - * - * @return status code - */ -esp_err_t httpd_set_sess_recv_override(httpd_handle_t hd, int sockfd, httpd_recv_func_t recv_func); +esp_err_t httpd_sess_set_send_override(httpd_handle_t hd, int sockfd, httpd_send_func_t send_func); /** * @brief Override web server's pending function (by session FD) * - * @see httpd_set_pending_override() + * This function overrides the web server's pending function. This function is + * used to test for pending bytes in a socket. * - * @param[in] hd HTTPD instance handle - * @param[in] fd session socket FD + * @note This API is supposed to be called either from the context of + * - an http session APIs where sockfd is a valid parameter + * - a URI handler where sockfd is obtained using httpd_req_to_sockfd() + * + * @param[in] hd HTTPD instance handle + * @param[in] sockfd Session socket FD * @param[in] pending_func The receive function to be set for this session * - * @return status code + * @return + * - ESP_OK : On successfully registering override + * - ESP_ERR_INVALID_ARG : Null arguments */ -esp_err_t httpd_set_sess_pending_override(httpd_handle_t hd, int sockfd, httpd_pending_func_t pending_func); +esp_err_t httpd_sess_set_pending_override(httpd_handle_t hd, int sockfd, httpd_pending_func_t pending_func); /** * @brief Get the Socket Descriptor from the HTTP request @@ -612,7 +584,7 @@ esp_err_t httpd_set_sess_pending_override(httpd_handle_t hd, int sockfd, httpd_p * This is useful when user wants to call functions that require * session socket fd, from within a URI handler, ie. : * httpd_sess_get_ctx(), - * httpd_trigger_sess_close(), + * httpd_sess_trigger_close(), * httpd_sess_update_timestamp(). * * @note This API is supposed to be called only from the context of @@ -1136,7 +1108,7 @@ void *httpd_get_global_transport_ctx(httpd_handle_t handle); * - ESP_ERR_NOT_FOUND : Socket fd not found * - ESP_ERR_INVALID_ARG : Null arguments */ -esp_err_t httpd_trigger_sess_close(httpd_handle_t handle, int sockfd); +esp_err_t httpd_sess_trigger_close(httpd_handle_t handle, int sockfd); /** * @brief Update timestamp for a given socket diff --git a/components/esp_http_server/src/esp_httpd_priv.h b/components/esp_http_server/src/esp_httpd_priv.h index 5ae18dfe5..68bc9d64d 100644 --- a/components/esp_http_server/src/esp_httpd_priv.h +++ b/components/esp_http_server/src/esp_httpd_priv.h @@ -240,6 +240,14 @@ esp_err_t httpd_sess_process(struct httpd_data *hd, int clifd); */ int httpd_sess_delete(struct httpd_data *hd, int clifd); +/** + * @brief Free session context + * + * @param[in] ctx Pointer to session context + * @param[in] free_fn Free function to call on session context + */ +void httpd_sess_free_ctx(void *ctx, httpd_free_ctx_fn_t free_fn); + /** * @brief Add descriptors present in the socket database to an fd_set and * update the value of maxfd which are needed by the select function @@ -351,7 +359,16 @@ void httpd_unregister_all_uri_handlers(struct httpd_data *hd); * - true : if valid request * - false : otherwise */ -bool httpd_valid_req(httpd_req_t *r); +bool httpd_validate_req_ptr(httpd_req_t *r); + +/* httpd_validate_req_ptr() adds some overhead to frequently used APIs, + * and is useful mostly for debugging, so it's preferable to disable + * the check by defaut and enable it only if necessary */ +#ifdef CONFIG_HTTPD_VALIDATE_REQ +#define httpd_valid_req(r) httpd_validate_req_ptr(r) +#else +#define httpd_valid_req(r) true +#endif /** End of Group : URI Handling * @} diff --git a/components/esp_http_server/src/httpd_parse.c b/components/esp_http_server/src/httpd_parse.c index 93b5abddc..51843182d 100644 --- a/components/esp_http_server/src/httpd_parse.c +++ b/components/esp_http_server/src/httpd_parse.c @@ -552,6 +552,24 @@ static void init_req_aux(struct httpd_req_aux *ra, httpd_config_t *config) memset(ra->resp_hdrs, 0, config->max_resp_headers * sizeof(struct resp_hdr)); } +static void httpd_req_cleanup(httpd_req_t *r) +{ + struct httpd_req_aux *ra = r->aux; + + /* Retrieve session info from the request into the socket database */ + if (ra->sd->ctx != r->sess_ctx) { + /* Free previous context */ + httpd_sess_free_ctx(ra->sd->ctx, ra->sd->free_ctx); + ra->sd->ctx = r->sess_ctx; + } + ra->sd->free_ctx = r->free_ctx; + + /* Clear out the request and request_aux structures */ + ra->sd = NULL; + r->handle = NULL; + r->aux = NULL; +} + /* Function that processes incoming TCP data and * updates the http request data httpd_req_t */ @@ -563,7 +581,7 @@ esp_err_t httpd_req_new(struct httpd_data *hd, struct sock_db *sd) r->handle = hd; r->aux = &hd->hd_req_aux; /* Associate the request to the socket */ - struct httpd_req_aux *ra = r->aux; + struct httpd_req_aux *ra = r->aux; ra->sd = sd; /* Set defaults */ ra->status = (char *)HTTPD_200; @@ -573,7 +591,11 @@ esp_err_t httpd_req_new(struct httpd_data *hd, struct sock_db *sd) r->sess_ctx = sd->ctx; r->free_ctx = sd->free_ctx; /* Parse request */ - return httpd_parse_req(hd); + esp_err_t err = httpd_parse_req(hd); + if (err != ESP_OK) { + httpd_req_cleanup(r); + } + return err; } /* Function that resets the http request data @@ -592,6 +614,7 @@ esp_err_t httpd_req_delete(struct httpd_data *hd) int recv_len = MIN(sizeof(dummy) - 1, ra->remaining_len); int ret = httpd_req_recv(r, dummy, recv_len); if (ret < 0) { + httpd_req_cleanup(r); return ESP_FAIL; } @@ -599,20 +622,14 @@ esp_err_t httpd_req_delete(struct httpd_data *hd) ESP_LOGD(TAG, LOG_FMT("purging data : %s"), dummy); } - /* Retrieve session info from the request into the socket database */ - ra->sd->ctx = r->sess_ctx; - ra->sd->free_ctx = r->free_ctx; - - /* Clear out the request and request_aux structures */ - ra->sd = NULL; - r->aux = NULL; + httpd_req_cleanup(r); return ESP_OK; } /* Validates the request to prevent users from calling APIs, that are to * be called only inside URI handler, outside the handler context */ -bool httpd_valid_req(httpd_req_t *r) +bool httpd_validate_req_ptr(httpd_req_t *r) { if (r) { struct httpd_data *hd = (struct httpd_data *) r->handle; diff --git a/components/esp_http_server/src/httpd_sess.c b/components/esp_http_server/src/httpd_sess.c index b53c4f928..8c0601fd4 100644 --- a/components/esp_http_server/src/httpd_sess.c +++ b/components/esp_http_server/src/httpd_sess.c @@ -33,11 +33,23 @@ bool httpd_is_sess_available(struct httpd_data *hd) return false; } -struct sock_db *httpd_sess_get(struct httpd_data *hd, int newfd) +struct sock_db *httpd_sess_get(struct httpd_data *hd, int sockfd) { + if (hd == NULL) { + return NULL; + } + + /* Check if called inside a request handler, and the + * session sockfd in use is same as the parameter */ + if ((hd->hd_req_aux.sd) && (hd->hd_req_aux.sd->fd == sockfd)) { + /* Just return the pointer to the sock_db + * corresponding to the request */ + return hd->hd_req_aux.sd; + } + int i; for (i = 0; i < hd->config.max_open_sockets; i++) { - if (hd->hd_sd[i].fd == newfd) { + if (hd->hd_sd[i].fd == sockfd) { return &hd->hd_sd[i]; } } @@ -74,41 +86,71 @@ esp_err_t httpd_sess_new(struct httpd_data *hd, int newfd) return ESP_FAIL; } +void httpd_sess_free_ctx(void *ctx, httpd_free_ctx_fn_t free_fn) +{ + if (ctx) { + if (free_fn) { + free_fn(ctx); + } else { + free(ctx); + } + } +} + void *httpd_sess_get_ctx(httpd_handle_t handle, int sockfd) { - if (handle == NULL) { - return NULL; - } - struct sock_db *sd = httpd_sess_get(handle, sockfd); if (sd == NULL) { return NULL; } + /* Check if the function has been called from inside a + * request handler, in which case fetch the context from + * the httpd_req_t structure */ + struct httpd_data *hd = (struct httpd_data *) handle; + if (hd->hd_req_aux.sd == sd) { + return hd->hd_req.sess_ctx; + } + return sd->ctx; } void httpd_sess_set_ctx(httpd_handle_t handle, int sockfd, void *ctx, httpd_free_ctx_fn_t free_fn) { - if (handle == NULL) { - return; - } - struct sock_db *sd = httpd_sess_get(handle, sockfd); if (sd == NULL) { return; } - sd->ctx = ctx; + /* Check if the function has been called from inside a + * request handler, in which case set the context inside + * the httpd_req_t structure */ + struct httpd_data *hd = (struct httpd_data *) handle; + if (hd->hd_req_aux.sd == sd) { + if (hd->hd_req.sess_ctx != ctx) { + /* Don't free previous context if it is in sockdb + * as it will be freed inside httpd_req_cleanup() */ + if (sd->ctx != hd->hd_req.sess_ctx) { + /* Free previous context */ + httpd_sess_free_ctx(hd->hd_req.sess_ctx, hd->hd_req.free_ctx); + } + hd->hd_req.sess_ctx = ctx; + } + hd->hd_req.free_ctx = free_fn; + return; + } + + /* Else set the context inside the sock_db structure */ + if (sd->ctx != ctx) { + /* Free previous context */ + httpd_sess_free_ctx(sd->ctx, sd->free_ctx); + sd->ctx = ctx; + } sd->free_ctx = free_fn; } void *httpd_sess_get_transport_ctx(httpd_handle_t handle, int sockfd) { - if (handle == NULL) { - return NULL; - } - struct sock_db *sd = httpd_sess_get(handle, sockfd); if (sd == NULL) { return NULL; @@ -119,16 +161,16 @@ void *httpd_sess_get_transport_ctx(httpd_handle_t handle, int sockfd) void httpd_sess_set_transport_ctx(httpd_handle_t handle, int sockfd, void *ctx, httpd_free_ctx_fn_t free_fn) { - if (handle == NULL) { - return; - } - struct sock_db *sd = httpd_sess_get(handle, sockfd); if (sd == NULL) { return; } - sd->transport_ctx = ctx; + if (sd->transport_ctx != ctx) { + /* Free previous transport context */ + httpd_sess_free_ctx(sd->transport_ctx, sd->free_transport_ctx); + sd->transport_ctx = ctx; + } sd->free_transport_ctx = free_fn; } @@ -296,7 +338,7 @@ esp_err_t httpd_sess_close_lru(struct httpd_data *hd) } } ESP_LOGD(TAG, LOG_FMT("fd = %d"), lru_fd); - return httpd_trigger_sess_close(hd, lru_fd); + return httpd_sess_trigger_close(hd, lru_fd); } int httpd_sess_iterate(struct httpd_data *hd, int start_fd) @@ -333,14 +375,9 @@ static void httpd_sess_close(void *arg) } } -esp_err_t httpd_trigger_sess_close(httpd_handle_t handle, int sockfd) +esp_err_t httpd_sess_trigger_close(httpd_handle_t handle, int sockfd) { - if (handle == NULL) { - return ESP_ERR_INVALID_ARG; - } - - struct httpd_data *hd = (struct httpd_data *) handle; - struct sock_db *sock_db = httpd_sess_get(hd, sockfd); + struct sock_db *sock_db = httpd_sess_get(handle, sockfd); if (sock_db) { return httpd_queue_work(handle, httpd_sess_close, sock_db); } diff --git a/components/esp_http_server/src/httpd_txrx.c b/components/esp_http_server/src/httpd_txrx.c index d7ab0339a..69a5ba004 100644 --- a/components/esp_http_server/src/httpd_txrx.c +++ b/components/esp_http_server/src/httpd_txrx.c @@ -22,75 +22,36 @@ static const char *TAG = "httpd_txrx"; -esp_err_t httpd_set_sess_send_override(httpd_handle_t hd, int sockfd, httpd_send_func_t send_func) +esp_err_t httpd_sess_set_send_override(httpd_handle_t hd, int sockfd, httpd_send_func_t send_func) { struct sock_db *sess = httpd_sess_get(hd, sockfd); - if (!sess) return ESP_ERR_INVALID_ARG; + if (!sess) { + return ESP_ERR_INVALID_ARG; + } sess->send_fn = send_func; return ESP_OK; } -esp_err_t httpd_set_sess_recv_override(httpd_handle_t hd, int sockfd, httpd_recv_func_t recv_func) +esp_err_t httpd_sess_set_recv_override(httpd_handle_t hd, int sockfd, httpd_recv_func_t recv_func) { struct sock_db *sess = httpd_sess_get(hd, sockfd); - if (!sess) return ESP_ERR_INVALID_ARG; + if (!sess) { + return ESP_ERR_INVALID_ARG; + } sess->recv_fn = recv_func; return ESP_OK; } -esp_err_t httpd_set_sess_pending_override(httpd_handle_t hd, int sockfd, httpd_pending_func_t pending_func) +esp_err_t httpd_sess_set_pending_override(httpd_handle_t hd, int sockfd, httpd_pending_func_t pending_func) { struct sock_db *sess = httpd_sess_get(hd, sockfd); - if (!sess) return ESP_ERR_INVALID_ARG; + if (!sess) { + return ESP_ERR_INVALID_ARG; + } sess->pending_fn = pending_func; return ESP_OK; } -esp_err_t httpd_set_send_override(httpd_req_t *r, httpd_send_func_t send_func) -{ - if (r == NULL || send_func == NULL) { - return ESP_ERR_INVALID_ARG; - } - - if (!httpd_valid_req(r)) { - return ESP_ERR_HTTPD_INVALID_REQ; - } - - struct httpd_req_aux *ra = r->aux; - ra->sd->send_fn = send_func; - return ESP_OK; -} - -esp_err_t httpd_set_recv_override(httpd_req_t *r, httpd_recv_func_t recv_func) -{ - if (r == NULL || recv_func == NULL) { - return ESP_ERR_INVALID_ARG; - } - - if (!httpd_valid_req(r)) { - return ESP_ERR_HTTPD_INVALID_REQ; - } - - struct httpd_req_aux *ra = r->aux; - ra->sd->recv_fn = recv_func; - return ESP_OK; -} - -esp_err_t httpd_set_pending_override(httpd_req_t *r, httpd_pending_func_t pending_func) -{ - if (r == NULL || pending_func == NULL) { - return ESP_ERR_INVALID_ARG; - } - - if (!httpd_valid_req(r)) { - return ESP_ERR_HTTPD_INVALID_REQ; - } - - struct httpd_req_aux *ra = r->aux; - ra->sd->pending_fn = pending_func; - return ESP_OK; -} - int httpd_send(httpd_req_t *r, const char *buf, size_t buf_len) { if (r == NULL || buf == NULL) { diff --git a/components/esp_http_server/src/httpd_uri.c b/components/esp_http_server/src/httpd_uri.c index 9652c9439..e31a6108f 100644 --- a/components/esp_http_server/src/httpd_uri.c +++ b/components/esp_http_server/src/httpd_uri.c @@ -201,8 +201,10 @@ esp_err_t httpd_uri(struct httpd_data *hd) if (uri == NULL) { switch (err) { case HTTPD_404_NOT_FOUND: + ESP_LOGW(TAG, LOG_FMT("URI '%s' not found"), req->uri); return httpd_resp_send_err(req, HTTPD_404_NOT_FOUND); case HTTPD_405_METHOD_NOT_ALLOWED: + ESP_LOGW(TAG, LOG_FMT("Method '%d' not allowed for URI '%s'"), req->method, req->uri); return httpd_resp_send_err(req, HTTPD_405_METHOD_NOT_ALLOWED); default: return ESP_FAIL;