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. * function for freeing the global user context, please specify that here.
*/ */
void * global_user_ctx; void * global_user_ctx;
/**
* Free function for global user context
*/
httpd_free_ctx_fn_t global_user_ctx_free_fn; 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. * It will be freed using free(), unless global_transport_ctx_free_fn is specified.
*/ */
void * global_transport_ctx; void * global_transport_ctx;
/**
* Free function for global transport context
*/
httpd_free_ctx_fn_t global_transport_ctx_free_fn; 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 * 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 * @note This API is supposed to be called either from the context of
* a URI handler where httpd_req_t* request pointer is valid. * - 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] hd HTTPD instance handle
* @param[in] recv_func The receive function to be set for this request * @param[in] sockfd Session socket FD
* @param[in] recv_func The receive function to be set for this session
* *
* @return * @return
* - ESP_OK : On successfully registering override * - ESP_OK : On successfully registering override
* - ESP_ERR_INVALID_ARG : Null arguments * - 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); 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
*
* 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);
/** /**
* @brief Override web server's send function (by session FD) * @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 * @note This API is supposed to be called either from the context of
* @param[in] fd session socket FD * - 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 * @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); 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 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);
/** /**
* @brief Override web server's pending function (by session FD) * @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 * @note This API is supposed to be called either from the context of
* @param[in] fd session socket FD * - 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 * @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 * @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 * This is useful when user wants to call functions that require
* session socket fd, from within a URI handler, ie. : * session socket fd, from within a URI handler, ie. :
* httpd_sess_get_ctx(), * httpd_sess_get_ctx(),
* httpd_trigger_sess_close(), * httpd_sess_trigger_close(),
* httpd_sess_update_timestamp(). * httpd_sess_update_timestamp().
* *
* @note This API is supposed to be called only from the context of * @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_NOT_FOUND : Socket fd not found
* - ESP_ERR_INVALID_ARG : Null arguments * - 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 * @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); 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 * @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 * 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 * - true : if valid request
* - false : otherwise * - 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 /** 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)); 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 /* Function that processes incoming TCP data and
* updates the http request data httpd_req_t * 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->handle = hd;
r->aux = &hd->hd_req_aux; r->aux = &hd->hd_req_aux;
/* Associate the request to the socket */ /* Associate the request to the socket */
struct httpd_req_aux *ra = r->aux; struct httpd_req_aux *ra = r->aux;
ra->sd = sd; ra->sd = sd;
/* Set defaults */ /* Set defaults */
ra->status = (char *)HTTPD_200; 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->sess_ctx = sd->ctx;
r->free_ctx = sd->free_ctx; r->free_ctx = sd->free_ctx;
/* Parse request */ /* 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 /* 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 recv_len = MIN(sizeof(dummy) - 1, ra->remaining_len);
int ret = httpd_req_recv(r, dummy, recv_len); int ret = httpd_req_recv(r, dummy, recv_len);
if (ret < 0) { if (ret < 0) {
httpd_req_cleanup(r);
return ESP_FAIL; 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); ESP_LOGD(TAG, LOG_FMT("purging data : %s"), dummy);
} }
/* Retrieve session info from the request into the socket database */ httpd_req_cleanup(r);
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;
return ESP_OK; return ESP_OK;
} }
/* Validates the request to prevent users from calling APIs, that are to /* Validates the request to prevent users from calling APIs, that are to
* be called only inside URI handler, outside the handler context * 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) { if (r) {
struct httpd_data *hd = (struct httpd_data *) r->handle; 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; 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; int i;
for (i = 0; i < hd->config.max_open_sockets; 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]; return &hd->hd_sd[i];
} }
} }
@ -74,41 +86,71 @@ esp_err_t httpd_sess_new(struct httpd_data *hd, int newfd)
return ESP_FAIL; 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) 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); struct sock_db *sd = httpd_sess_get(handle, sockfd);
if (sd == NULL) { if (sd == NULL) {
return 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; return sd->ctx;
} }
void httpd_sess_set_ctx(httpd_handle_t handle, int sockfd, void *ctx, httpd_free_ctx_fn_t free_fn) 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); struct sock_db *sd = httpd_sess_get(handle, sockfd);
if (sd == NULL) { if (sd == NULL) {
return; 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; sd->free_ctx = free_fn;
} }
void *httpd_sess_get_transport_ctx(httpd_handle_t handle, int sockfd) 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); struct sock_db *sd = httpd_sess_get(handle, sockfd);
if (sd == NULL) { if (sd == NULL) {
return 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) 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); struct sock_db *sd = httpd_sess_get(handle, sockfd);
if (sd == NULL) { if (sd == NULL) {
return; 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; 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); 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) 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) { struct sock_db *sock_db = httpd_sess_get(handle, sockfd);
return ESP_ERR_INVALID_ARG;
}
struct httpd_data *hd = (struct httpd_data *) handle;
struct sock_db *sock_db = httpd_sess_get(hd, sockfd);
if (sock_db) { if (sock_db) {
return httpd_queue_work(handle, httpd_sess_close, sock_db); return httpd_queue_work(handle, httpd_sess_close, sock_db);
} }

View file

@ -22,75 +22,36 @@
static const char *TAG = "httpd_txrx"; 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); 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; sess->send_fn = send_func;
return ESP_OK; 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); 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; sess->recv_fn = recv_func;
return ESP_OK; 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); 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; sess->pending_fn = pending_func;
return ESP_OK; 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) int httpd_send(httpd_req_t *r, const char *buf, size_t buf_len)
{ {
if (r == NULL || buf == NULL) { if (r == NULL || buf == NULL) {

View file

@ -201,8 +201,10 @@ esp_err_t httpd_uri(struct httpd_data *hd)
if (uri == NULL) { if (uri == NULL) {
switch (err) { switch (err) {
case HTTPD_404_NOT_FOUND: 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); return httpd_resp_send_err(req, HTTPD_404_NOT_FOUND);
case HTTPD_405_METHOD_NOT_ALLOWED: 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); return httpd_resp_send_err(req, HTTPD_405_METHOD_NOT_ALLOWED);
default: default:
return ESP_FAIL; return ESP_FAIL;