* 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
* 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;
/**
* 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;
/**
*/
/**
- * @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);
+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
+ * @brief Override web server's send function (by session FD)
*
* 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.
+ * @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] send_func The send function to be set for this request
+ * @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
* - 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);
+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
+ * @brief Override web server's pending function (by session FD)
*
* 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.
+ * @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] pending_func The pending function to be set for this request
+ * @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
* - 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)
- *
- * @see httpd_set_send_override()
- *
- * @param[in] hd HTTPD instance handle
- * @param[in] fd session socket FD
- * @param[in] send_func The send function to be set for this session
- *
- * @return status code
- */
-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);
-
-/**
- * @brief Override web server's pending function (by session FD)
- *
- * @see httpd_set_pending_override()
- *
- * @param[in] hd HTTPD instance handle
- * @param[in] fd session socket FD
- * @param[in] pending_func The receive function to be set for this session
- *
- * @return status code
*/
-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
* 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
* - 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
*/
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
* - 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
* @}
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
*/
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;
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
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;
}
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;
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];
}
}
return ESP_FAIL;
}
-void *httpd_sess_get_ctx(httpd_handle_t handle, int sockfd)
+void httpd_sess_free_ctx(void *ctx, httpd_free_ctx_fn_t free_fn)
{
- if (handle == NULL) {
- return NULL;
+ if (ctx) {
+ if (free_fn) {
+ free_fn(ctx);
+ } else {
+ free(ctx);
+ }
}
+}
+void *httpd_sess_get_ctx(httpd_handle_t handle, int sockfd)
+{
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) {
+ struct sock_db *sd = httpd_sess_get(handle, sockfd);
+ if (sd == NULL) {
return;
}
- struct sock_db *sd = httpd_sess_get(handle, sockfd);
- if (sd == NULL) {
+ /* 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;
}
- sd->ctx = ctx;
+ /* 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;
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;
}
}
}
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)
}
}
-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);
}
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;
- 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)
-{
- struct sock_db *sess = httpd_sess_get(hd, sockfd);
- 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)
-{
- struct sock_db *sess = httpd_sess_get(hd, sockfd);
- 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) {
+ if (!sess) {
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;
+ sess->send_fn = send_func;
return ESP_OK;
}
-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)
{
- if (r == NULL || recv_func == NULL) {
+ struct sock_db *sess = httpd_sess_get(hd, sockfd);
+ if (!sess) {
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;
+ sess->recv_fn = recv_func;
return ESP_OK;
}
-esp_err_t httpd_set_pending_override(httpd_req_t *r, 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)
{
- if (r == NULL || pending_func == NULL) {
+ struct sock_db *sess = httpd_sess_get(hd, sockfd);
+ if (!sess) {
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;
+ sess->pending_fn = pending_func;
return ESP_OK;
}
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;