]> granicus.if.org Git - esp-idf/commitdiff
esp_http_server : APIs renamed and context get/set implementations fixed
authorAnurag Kar <anurag.kar@espressif.com>
Fri, 2 Nov 2018 18:25:40 +0000 (23:55 +0530)
committerAnurag Kar <anurag.kar@espressif.com>
Sun, 3 Feb 2019 16:20:35 +0000 (21:50 +0530)
* 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

components/esp_http_server/include/esp_http_server.h
components/esp_http_server/src/esp_httpd_priv.h
components/esp_http_server/src/httpd_parse.c
components/esp_http_server/src/httpd_sess.c
components/esp_http_server/src/httpd_txrx.c
components/esp_http_server/src/httpd_uri.c

index 519538f9ab325b745a65165e3fab2bb497b3d688..793f3dd806f8f9d1c38f5800d846fa309ef4478a 100644 (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);
+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
@@ -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
index 5ae18dfe512a5cb9a25cf7d4855283c447316b59..68bc9d64de6adbae3525e71ebee8c32e4a8cb6fc 100644 (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
  * @}
index 93b5abddc347d1a2855639ac7798775a2f2b5e0a..51843182dd6e13b43fe4440e30d89dfa7a062e7e 100644 (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
  */
@@ -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;
index b53c4f9281534e60da13997dfa07228dcc5f914d..8c0601fd4e12fc978329727cce6a629ac74ca607 100644 (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_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;
@@ -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);
     }
index d7ab0339a6dd5f7c8422e440c155d4b6e0dc6a4c..69a5ba0048908cf8ad3d3a8cb2eb369a77aa63d5 100644 (file)
 
 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;
 }
 
index 9652c9439a933336d6e57d5a6bdc53227bca3f94..e31a6108fb7929b9aa87615fc365f56960199bab 100644 (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;