esp_http_server : APIs renamed and context get/set implementations fixed

* http_sess_set_*_override APIs are now the only ones available to set custom recv/send/pending functions
* Fixed side effects to using http_sess_set/get_context inside URI handlers
This commit is contained in:
Anurag Kar 2018-11-02 23:55:40 +05:30
parent 900dbfd1f6
commit 9c3262f80b
6 changed files with 167 additions and 161 deletions

View file

@ -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.
*
* @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] fd session socket FD
* @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.
*
* @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] fd session socket FD
* @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

View file

@ -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
* @}

View file

@ -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
*/
@ -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;

View file

@ -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;
}
/* 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;
}
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);
}

View file

@ -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) {

View file

@ -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;