|
|
@@ -11,49 +11,183 @@
|
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
// See the License for the specific language governing permissions and
|
|
|
// limitations under the License.
|
|
|
-
|
|
|
-
|
|
|
#include <stdlib.h>
|
|
|
#include <esp_log.h>
|
|
|
#include <esp_err.h>
|
|
|
+#include <fcntl.h>
|
|
|
+#include <errno.h>
|
|
|
+#include <unistd.h>
|
|
|
|
|
|
#include <esp_http_server.h>
|
|
|
#include "esp_httpd_priv.h"
|
|
|
|
|
|
static const char *TAG = "httpd_sess";
|
|
|
|
|
|
-bool httpd_is_sess_available(struct httpd_data *hd)
|
|
|
+typedef enum {
|
|
|
+ HTTPD_TASK_NONE = 0,
|
|
|
+ HTTPD_TASK_INIT, // Init session
|
|
|
+ HTTPD_TASK_GET_ACTIVE, // Get active session (fd!=-1)
|
|
|
+ HTTPD_TASK_GET_FREE, // Get free session slot (fd<0)
|
|
|
+ HTTPD_TASK_FIND_FD, // Find session with specific fd
|
|
|
+ HTTPD_TASK_SET_DESCRIPTOR, // Set descriptor
|
|
|
+ HTTPD_TASK_DELETE_INVALID, // Delete invalid session
|
|
|
+ HTTPD_TASK_FIND_LOWEST_LRU, // Find session with lowest lru
|
|
|
+ HTTPD_TASK_CLOSE // Close session
|
|
|
+} task_t;
|
|
|
+
|
|
|
+typedef struct {
|
|
|
+ task_t task;
|
|
|
+ int fd;
|
|
|
+ fd_set *fdset;
|
|
|
+ int max_fd;
|
|
|
+ struct httpd_data *hd;
|
|
|
+ uint64_t lru_counter;
|
|
|
+ struct sock_db *session;
|
|
|
+} enum_context_t;
|
|
|
+
|
|
|
+void httpd_sess_enum(struct httpd_data *hd, httpd_session_enum_function enum_function, void *context)
|
|
|
{
|
|
|
- int i;
|
|
|
- for (i = 0; i < hd->config.max_open_sockets; i++) {
|
|
|
- if (hd->hd_sd[i].fd == -1) {
|
|
|
- return true;
|
|
|
+ if ((!hd) || (!hd->hd_sd) || (!hd->config.max_open_sockets)) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ struct sock_db *current = hd->hd_sd;
|
|
|
+ struct sock_db *end = hd->hd_sd + hd->config.max_open_sockets - 1;
|
|
|
+
|
|
|
+ while (current <= end) {
|
|
|
+ if (enum_function && (!enum_function(current, context))) {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ current++;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// Check if a FD is valid
|
|
|
+static int fd_is_valid(int fd)
|
|
|
+{
|
|
|
+ return fcntl(fd, F_GETFD) != -1 || errno != EBADF;
|
|
|
+}
|
|
|
+
|
|
|
+static int enum_function(struct sock_db *session, void *context)
|
|
|
+{
|
|
|
+ if ((!session) || (!context)) {
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ enum_context_t *ctx = (enum_context_t *) context;
|
|
|
+ int found = 0;
|
|
|
+ switch (ctx->task) {
|
|
|
+ // Initialize session
|
|
|
+ case HTTPD_TASK_INIT:
|
|
|
+ session->fd = -1;
|
|
|
+ session->ctx = NULL;
|
|
|
+ break;
|
|
|
+ // Get active session
|
|
|
+ case HTTPD_TASK_GET_ACTIVE:
|
|
|
+ found = (session->fd != -1);
|
|
|
+ break;
|
|
|
+ // Get free slot
|
|
|
+ case HTTPD_TASK_GET_FREE:
|
|
|
+ found = (session->fd < 0);
|
|
|
+ break;
|
|
|
+ // Find fd
|
|
|
+ case HTTPD_TASK_FIND_FD:
|
|
|
+ found = (session->fd == ctx->fd);
|
|
|
+ break;
|
|
|
+ // Set descriptor
|
|
|
+ case HTTPD_TASK_SET_DESCRIPTOR:
|
|
|
+ if (session->fd != -1) {
|
|
|
+ FD_SET(session->fd, ctx->fdset);
|
|
|
+ if (session->fd > ctx->max_fd) {
|
|
|
+ ctx->max_fd = session->fd;
|
|
|
+ }
|
|
|
}
|
|
|
+ break;
|
|
|
+ // Delete invalid session
|
|
|
+ case HTTPD_TASK_DELETE_INVALID:
|
|
|
+ if (!fd_is_valid(session->fd)) {
|
|
|
+ ESP_LOGW(TAG, LOG_FMT("Closing invalid socket %d"), session->fd);
|
|
|
+ httpd_sess_delete(ctx->hd, session);
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ // Find lowest lru
|
|
|
+ case HTTPD_TASK_FIND_LOWEST_LRU:
|
|
|
+ // Found free slot - no need to check other sessions
|
|
|
+ if (session->fd == -1) {
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ // Check/update lowest lru
|
|
|
+ if (session->lru_counter < ctx->lru_counter) {
|
|
|
+ ctx->lru_counter = session->lru_counter;
|
|
|
+ ctx->session = session;
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case HTTPD_TASK_CLOSE:
|
|
|
+ if (session->fd != -1) {
|
|
|
+ ESP_LOGD(TAG, LOG_FMT("cleaning up socket %d"), session->fd);
|
|
|
+ httpd_sess_delete(ctx->hd, session);
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ return 0;
|
|
|
}
|
|
|
- return false;
|
|
|
+ if (found) {
|
|
|
+ ctx->session = session;
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ return 1;
|
|
|
+}
|
|
|
+
|
|
|
+static void httpd_sess_close(void *arg)
|
|
|
+{
|
|
|
+ struct sock_db *sock_db = (struct sock_db *) arg;
|
|
|
+ if (!sock_db) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!sock_db->lru_counter && !sock_db->lru_socket) {
|
|
|
+ ESP_LOGD(TAG, "Skipping session close for %d as it seems to be a race condition", sock_db->fd);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ sock_db->lru_socket = false;
|
|
|
+ struct httpd_data *hd = (struct httpd_data *) sock_db->handle;
|
|
|
+ httpd_sess_delete(hd, sock_db);
|
|
|
+}
|
|
|
+
|
|
|
+struct sock_db *httpd_sess_get_free(struct httpd_data *hd)
|
|
|
+{
|
|
|
+ if ((!hd) || (hd->hd_sd_active_count == hd->config.max_open_sockets)) {
|
|
|
+ return NULL;
|
|
|
+ }
|
|
|
+ enum_context_t context = {
|
|
|
+ .task = HTTPD_TASK_GET_FREE
|
|
|
+ };
|
|
|
+ httpd_sess_enum(hd, enum_function, &context);
|
|
|
+ return context.session;
|
|
|
+}
|
|
|
+
|
|
|
+bool httpd_is_sess_available(struct httpd_data *hd)
|
|
|
+{
|
|
|
+ return httpd_sess_get_free(hd) ? true : false;
|
|
|
}
|
|
|
|
|
|
struct sock_db *httpd_sess_get(struct httpd_data *hd, int sockfd)
|
|
|
{
|
|
|
- if (hd == NULL) {
|
|
|
+ if ((!hd) || (!hd->hd_sd) || (!hd->config.max_open_sockets)) {
|
|
|
return NULL;
|
|
|
}
|
|
|
|
|
|
- /* Check if called inside a request handler, and the
|
|
|
- * session sockfd in use is same as the parameter */
|
|
|
+ // Check if called inside a request handler, and the session sockfd in use is same as the parameter
|
|
|
+ // => Just return the pointer to the sock_db corresponding to the request
|
|
|
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 == sockfd) {
|
|
|
- return &hd->hd_sd[i];
|
|
|
- }
|
|
|
- }
|
|
|
- return NULL;
|
|
|
+ enum_context_t context = {
|
|
|
+ .task = HTTPD_TASK_FIND_FD,
|
|
|
+ .fd = sockfd
|
|
|
+ };
|
|
|
+ httpd_sess_enum(hd, enum_function, &context);
|
|
|
+ return context.session;
|
|
|
}
|
|
|
|
|
|
esp_err_t httpd_sess_new(struct httpd_data *hd, int newfd)
|
|
|
@@ -65,78 +199,102 @@ esp_err_t httpd_sess_new(struct httpd_data *hd, int newfd)
|
|
|
return ESP_FAIL;
|
|
|
}
|
|
|
|
|
|
- int i;
|
|
|
- for (i = 0; i < hd->config.max_open_sockets; i++) {
|
|
|
- if (hd->hd_sd[i].fd == -1) {
|
|
|
- memset(&hd->hd_sd[i], 0, sizeof(hd->hd_sd[i]));
|
|
|
- hd->hd_sd[i].fd = newfd;
|
|
|
- hd->hd_sd[i].handle = (httpd_handle_t) hd;
|
|
|
- hd->hd_sd[i].send_fn = httpd_default_send;
|
|
|
- hd->hd_sd[i].recv_fn = httpd_default_recv;
|
|
|
-
|
|
|
- /* Call user-defined session opening function */
|
|
|
- if (hd->config.open_fn) {
|
|
|
- esp_err_t ret = hd->config.open_fn(hd, hd->hd_sd[i].fd);
|
|
|
- if (ret != ESP_OK) {
|
|
|
- httpd_sess_delete(hd, hd->hd_sd[i].fd);
|
|
|
- ESP_LOGD(TAG, LOG_FMT("open_fn failed for fd = %d"), newfd);
|
|
|
- return ret;
|
|
|
- }
|
|
|
- }
|
|
|
- return ESP_OK;
|
|
|
+ struct sock_db *session = httpd_sess_get_free(hd);
|
|
|
+ if (!session) {
|
|
|
+ ESP_LOGD(TAG, LOG_FMT("unable to launch session for fd = %d"), newfd);
|
|
|
+ return ESP_FAIL;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Clear session data
|
|
|
+ memset(session, 0, sizeof (struct sock_db));
|
|
|
+ session->fd = newfd;
|
|
|
+ session->handle = (httpd_handle_t) hd;
|
|
|
+ session->send_fn = httpd_default_send;
|
|
|
+ session->recv_fn = httpd_default_recv;
|
|
|
+
|
|
|
+ // Call user-defined session opening function
|
|
|
+ if (hd->config.open_fn) {
|
|
|
+ esp_err_t ret = hd->config.open_fn(hd, session->fd);
|
|
|
+ if (ret != ESP_OK) {
|
|
|
+ httpd_sess_delete(hd, session);
|
|
|
+ ESP_LOGD(TAG, LOG_FMT("open_fn failed for fd = %d"), newfd);
|
|
|
+ return ret;
|
|
|
}
|
|
|
}
|
|
|
- ESP_LOGD(TAG, LOG_FMT("unable to launch session for fd = %d"), newfd);
|
|
|
- return ESP_FAIL;
|
|
|
+
|
|
|
+ // increment number of sessions
|
|
|
+ hd->hd_sd_active_count++;
|
|
|
+ ESP_LOGD(TAG, LOG_FMT("active sockets: %d"), hd->hd_sd_active_count);
|
|
|
+
|
|
|
+ return ESP_OK;
|
|
|
}
|
|
|
|
|
|
-void httpd_sess_free_ctx(void *ctx, httpd_free_ctx_fn_t free_fn)
|
|
|
+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);
|
|
|
- }
|
|
|
+ if ((!ctx) || (!*ctx)) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ if (free_fn) {
|
|
|
+ free_fn(*ctx);
|
|
|
+ } else {
|
|
|
+ free(*ctx);
|
|
|
+ }
|
|
|
+ *ctx = NULL;
|
|
|
+}
|
|
|
+
|
|
|
+void httpd_sess_clear_ctx(struct sock_db *session)
|
|
|
+{
|
|
|
+ if ((!session) || (!session->ctx)) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // free user ctx
|
|
|
+ if (session->ctx) {
|
|
|
+ httpd_sess_free_ctx(&session->ctx, session->free_ctx);
|
|
|
+ session->free_ctx = NULL;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Free 'transport' context
|
|
|
+ if (session->transport_ctx) {
|
|
|
+ httpd_sess_free_ctx(&session->transport_ctx, session->free_transport_ctx);
|
|
|
+ session->free_transport_ctx = NULL;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
void *httpd_sess_get_ctx(httpd_handle_t handle, int sockfd)
|
|
|
{
|
|
|
- struct sock_db *sd = httpd_sess_get(handle, sockfd);
|
|
|
- if (sd == NULL) {
|
|
|
+ struct sock_db *session = httpd_sess_get(handle, sockfd);
|
|
|
+ if (!session) {
|
|
|
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 */
|
|
|
+ // 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) {
|
|
|
+ if (hd->hd_req_aux.sd == session) {
|
|
|
return hd->hd_req.sess_ctx;
|
|
|
}
|
|
|
-
|
|
|
- return sd->ctx;
|
|
|
+ return session->ctx;
|
|
|
}
|
|
|
|
|
|
void httpd_sess_set_ctx(httpd_handle_t handle, int sockfd, void *ctx, httpd_free_ctx_fn_t free_fn)
|
|
|
{
|
|
|
- struct sock_db *sd = httpd_sess_get(handle, sockfd);
|
|
|
- if (sd == NULL) {
|
|
|
+ struct sock_db *session = httpd_sess_get(handle, sockfd);
|
|
|
+ if (!session) {
|
|
|
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 */
|
|
|
+ // 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_aux.sd == session) {
|
|
|
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);
|
|
|
+ // Don't free previous context if it is in sockdb
|
|
|
+ // as it will be freed inside httpd_req_cleanup()
|
|
|
+ if (session->ctx != hd->hd_req.sess_ctx) {
|
|
|
+ httpd_sess_free_ctx(&hd->hd_req.sess_ctx, hd->hd_req.free_ctx); // Free previous context
|
|
|
}
|
|
|
hd->hd_req.sess_ctx = ctx;
|
|
|
}
|
|
|
@@ -144,164 +302,122 @@ void httpd_sess_set_ctx(httpd_handle_t handle, int sockfd, void *ctx, httpd_free
|
|
|
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;
|
|
|
+ // Else set the context inside the sock_db structure
|
|
|
+ if (session->ctx != ctx) {
|
|
|
+ // Free previous context
|
|
|
+ httpd_sess_free_ctx(&session->ctx, session->free_ctx);
|
|
|
+ session->ctx = ctx;
|
|
|
}
|
|
|
- sd->free_ctx = free_fn;
|
|
|
+ session->free_ctx = free_fn;
|
|
|
}
|
|
|
|
|
|
void *httpd_sess_get_transport_ctx(httpd_handle_t handle, int sockfd)
|
|
|
{
|
|
|
- struct sock_db *sd = httpd_sess_get(handle, sockfd);
|
|
|
- if (sd == NULL) {
|
|
|
- return NULL;
|
|
|
- }
|
|
|
-
|
|
|
- return sd->transport_ctx;
|
|
|
+ struct sock_db *session = httpd_sess_get(handle, sockfd);
|
|
|
+ return session ? session->transport_ctx : NULL;
|
|
|
}
|
|
|
|
|
|
void httpd_sess_set_transport_ctx(httpd_handle_t handle, int sockfd, void *ctx, httpd_free_ctx_fn_t free_fn)
|
|
|
{
|
|
|
- struct sock_db *sd = httpd_sess_get(handle, sockfd);
|
|
|
- if (sd == NULL) {
|
|
|
+ struct sock_db *session = httpd_sess_get(handle, sockfd);
|
|
|
+ if (!session) {
|
|
|
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;
|
|
|
+ if (session->transport_ctx != ctx) {
|
|
|
+ // Free previous transport context
|
|
|
+ httpd_sess_free_ctx(&session->transport_ctx, session->free_transport_ctx);
|
|
|
+ session->transport_ctx = ctx;
|
|
|
}
|
|
|
- sd->free_transport_ctx = free_fn;
|
|
|
+ session->free_transport_ctx = free_fn;
|
|
|
}
|
|
|
|
|
|
-void httpd_sess_set_descriptors(struct httpd_data *hd,
|
|
|
- fd_set *fdset, int *maxfd)
|
|
|
+void httpd_sess_set_descriptors(struct httpd_data *hd, fd_set *fdset, int *maxfd)
|
|
|
{
|
|
|
- int i;
|
|
|
- *maxfd = -1;
|
|
|
- for (i = 0; i < hd->config.max_open_sockets; i++) {
|
|
|
- if (hd->hd_sd[i].fd != -1) {
|
|
|
- FD_SET(hd->hd_sd[i].fd, fdset);
|
|
|
- if (hd->hd_sd[i].fd > *maxfd) {
|
|
|
- *maxfd = hd->hd_sd[i].fd;
|
|
|
- }
|
|
|
- }
|
|
|
+ enum_context_t context = {
|
|
|
+ .task = HTTPD_TASK_SET_DESCRIPTOR,
|
|
|
+ .max_fd = -1,
|
|
|
+ .fdset = fdset
|
|
|
+ };
|
|
|
+ httpd_sess_enum(hd, enum_function, &context);
|
|
|
+ if (maxfd) {
|
|
|
+ *maxfd = context.max_fd;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-/** Check if a FD is valid */
|
|
|
-static int fd_is_valid(int fd)
|
|
|
-{
|
|
|
- return fcntl(fd, F_GETFD) != -1 || errno != EBADF;
|
|
|
-}
|
|
|
-
|
|
|
-static inline uint64_t httpd_sess_get_lru_counter(void)
|
|
|
+void httpd_sess_delete_invalid(struct httpd_data *hd)
|
|
|
{
|
|
|
- static uint64_t lru_counter = 0;
|
|
|
- return ++lru_counter;
|
|
|
+ enum_context_t context = {
|
|
|
+ .task = HTTPD_TASK_DELETE_INVALID,
|
|
|
+ .hd = hd
|
|
|
+ };
|
|
|
+ httpd_sess_enum(hd, enum_function, &context);
|
|
|
}
|
|
|
|
|
|
-void httpd_sess_delete_invalid(struct httpd_data *hd)
|
|
|
+void httpd_sess_delete(struct httpd_data *hd, struct sock_db *session)
|
|
|
{
|
|
|
- for (int i = 0; i < hd->config.max_open_sockets; i++) {
|
|
|
- if (hd->hd_sd[i].fd != -1 && !fd_is_valid(hd->hd_sd[i].fd)) {
|
|
|
- ESP_LOGW(TAG, LOG_FMT("Closing invalid socket %d"), hd->hd_sd[i].fd);
|
|
|
- httpd_sess_delete(hd, hd->hd_sd[i].fd);
|
|
|
- }
|
|
|
+ if ((!hd) || (!session) || (session->fd < 0)) {
|
|
|
+ return;
|
|
|
}
|
|
|
-}
|
|
|
|
|
|
-int httpd_sess_delete(struct httpd_data *hd, int fd)
|
|
|
-{
|
|
|
- ESP_LOGD(TAG, LOG_FMT("fd = %d"), fd);
|
|
|
- int i;
|
|
|
- int pre_sess_fd = -1;
|
|
|
- for (i = 0; i < hd->config.max_open_sockets; i++) {
|
|
|
- if (hd->hd_sd[i].fd == fd) {
|
|
|
- /* global close handler */
|
|
|
- if (hd->config.close_fn) {
|
|
|
- hd->config.close_fn(hd, fd);
|
|
|
- }
|
|
|
+ ESP_LOGD(TAG, LOG_FMT("fd = %d"), session->fd);
|
|
|
|
|
|
- /* release 'user' context */
|
|
|
- if (hd->hd_sd[i].ctx) {
|
|
|
- if (hd->hd_sd[i].free_ctx) {
|
|
|
- hd->hd_sd[i].free_ctx(hd->hd_sd[i].ctx);
|
|
|
- } else {
|
|
|
- free(hd->hd_sd[i].ctx);
|
|
|
- }
|
|
|
- hd->hd_sd[i].ctx = NULL;
|
|
|
- hd->hd_sd[i].free_ctx = NULL;
|
|
|
- }
|
|
|
+ // Call close function if defined
|
|
|
+ if (hd->config.close_fn) {
|
|
|
+ hd->config.close_fn(hd, session->fd);
|
|
|
+ } else {
|
|
|
+ close(session->fd);
|
|
|
+ }
|
|
|
|
|
|
- /* release 'transport' context */
|
|
|
- if (hd->hd_sd[i].transport_ctx) {
|
|
|
- if (hd->hd_sd[i].free_transport_ctx) {
|
|
|
- hd->hd_sd[i].free_transport_ctx(hd->hd_sd[i].transport_ctx);
|
|
|
- } else {
|
|
|
- free(hd->hd_sd[i].transport_ctx);
|
|
|
- }
|
|
|
- hd->hd_sd[i].transport_ctx = NULL;
|
|
|
- hd->hd_sd[i].free_transport_ctx = NULL;
|
|
|
- }
|
|
|
+ // clear all contexts
|
|
|
+ httpd_sess_clear_ctx(session);
|
|
|
|
|
|
- /* mark session slot as available */
|
|
|
- hd->hd_sd[i].fd = -1;
|
|
|
- break;
|
|
|
- } else if (hd->hd_sd[i].fd != -1) {
|
|
|
- /* Return the fd just preceding the one being
|
|
|
- * deleted so that iterator can continue from
|
|
|
- * the correct fd */
|
|
|
- pre_sess_fd = hd->hd_sd[i].fd;
|
|
|
- }
|
|
|
+ // mark session slot as available
|
|
|
+ session->fd = -1;
|
|
|
+
|
|
|
+ // decrement number of sessions
|
|
|
+ hd->hd_sd_active_count--;
|
|
|
+ ESP_LOGD(TAG, LOG_FMT("active sockets: %d"), hd->hd_sd_active_count);
|
|
|
+ if (!hd->hd_sd_active_count) {
|
|
|
+ hd->lru_counter = 0;
|
|
|
}
|
|
|
- return pre_sess_fd;
|
|
|
}
|
|
|
|
|
|
void httpd_sess_init(struct httpd_data *hd)
|
|
|
{
|
|
|
- int i;
|
|
|
- for (i = 0; i < hd->config.max_open_sockets; i++) {
|
|
|
- hd->hd_sd[i].fd = -1;
|
|
|
- hd->hd_sd[i].ctx = NULL;
|
|
|
- }
|
|
|
+ enum_context_t context = {
|
|
|
+ .task = HTTPD_TASK_INIT
|
|
|
+ };
|
|
|
+ httpd_sess_enum(hd, enum_function, &context);
|
|
|
}
|
|
|
|
|
|
-bool httpd_sess_pending(struct httpd_data *hd, int fd)
|
|
|
+bool httpd_sess_pending(struct httpd_data *hd, struct sock_db *session)
|
|
|
{
|
|
|
- struct sock_db *sd = httpd_sess_get(hd, fd);
|
|
|
- if (! sd) {
|
|
|
- return ESP_FAIL;
|
|
|
+ if (!session) {
|
|
|
+ return false;
|
|
|
}
|
|
|
-
|
|
|
- if (sd->pending_fn) {
|
|
|
+ if (session->pending_fn) {
|
|
|
// test if there's any data to be read (besides read() function, which is handled by select() in the main httpd loop)
|
|
|
// this should check e.g. for the SSL data buffer
|
|
|
- if (sd->pending_fn(hd, fd) > 0) {
|
|
|
+ if (session->pending_fn(hd, session->fd) > 0) {
|
|
|
return true;
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
- return (sd->pending_len != 0);
|
|
|
+ return (session->pending_len != 0);
|
|
|
}
|
|
|
|
|
|
/* This MUST return ESP_OK on successful execution. If any other
|
|
|
* value is returned, everything related to this socket will be
|
|
|
* cleaned up and the socket will be closed.
|
|
|
*/
|
|
|
-esp_err_t httpd_sess_process(struct httpd_data *hd, int newfd)
|
|
|
+esp_err_t httpd_sess_process(struct httpd_data *hd, struct sock_db *session)
|
|
|
{
|
|
|
- struct sock_db *sd = httpd_sess_get(hd, newfd);
|
|
|
- if (! sd) {
|
|
|
+ if ((!hd) || (!session)) {
|
|
|
return ESP_FAIL;
|
|
|
}
|
|
|
|
|
|
ESP_LOGD(TAG, LOG_FMT("httpd_req_new"));
|
|
|
- if (httpd_req_new(hd, sd) != ESP_OK) {
|
|
|
+ if (httpd_req_new(hd, session) != ESP_OK) {
|
|
|
return ESP_FAIL;
|
|
|
}
|
|
|
ESP_LOGD(TAG, LOG_FMT("httpd_req_delete"));
|
|
|
@@ -309,7 +425,7 @@ esp_err_t httpd_sess_process(struct httpd_data *hd, int newfd)
|
|
|
return ESP_FAIL;
|
|
|
}
|
|
|
ESP_LOGD(TAG, LOG_FMT("success"));
|
|
|
- sd->lru_counter = httpd_sess_get_lru_counter();
|
|
|
+ session->lru_counter = ++hd->lru_counter;
|
|
|
return ESP_OK;
|
|
|
}
|
|
|
|
|
|
@@ -319,87 +435,58 @@ esp_err_t httpd_sess_update_lru_counter(httpd_handle_t handle, int sockfd)
|
|
|
return ESP_ERR_INVALID_ARG;
|
|
|
}
|
|
|
|
|
|
- /* Search for the socket database entry */
|
|
|
struct httpd_data *hd = (struct httpd_data *) handle;
|
|
|
- int i;
|
|
|
- for (i = 0; i < hd->config.max_open_sockets; i++) {
|
|
|
- if (hd->hd_sd[i].fd == sockfd) {
|
|
|
- hd->hd_sd[i].lru_counter = httpd_sess_get_lru_counter();
|
|
|
- return ESP_OK;
|
|
|
- }
|
|
|
+
|
|
|
+ enum_context_t context = {
|
|
|
+ .task = HTTPD_TASK_FIND_FD,
|
|
|
+ .fd = sockfd
|
|
|
+ };
|
|
|
+ httpd_sess_enum(hd, enum_function, &context);
|
|
|
+ if (context.session) {
|
|
|
+ context.session->lru_counter = ++hd->lru_counter;
|
|
|
+ return ESP_OK;
|
|
|
}
|
|
|
return ESP_ERR_NOT_FOUND;
|
|
|
}
|
|
|
|
|
|
esp_err_t httpd_sess_close_lru(struct httpd_data *hd)
|
|
|
{
|
|
|
- uint64_t lru_counter = UINT64_MAX;
|
|
|
- int lru_fd = -1;
|
|
|
- int i;
|
|
|
- for (i = 0; i < hd->config.max_open_sockets; i++) {
|
|
|
- /* If a descriptor is -1, there is no need to close any session.
|
|
|
- * So, we can return from here, without finding the Least Recently Used
|
|
|
- * session
|
|
|
- */
|
|
|
- if (hd->hd_sd[i].fd == -1) {
|
|
|
- return ESP_OK;
|
|
|
- }
|
|
|
- if (hd->hd_sd[i].lru_counter < lru_counter) {
|
|
|
- lru_counter = hd->hd_sd[i].lru_counter;
|
|
|
- lru_fd = hd->hd_sd[i].fd;
|
|
|
- }
|
|
|
+ enum_context_t context = {
|
|
|
+ .task = HTTPD_TASK_FIND_LOWEST_LRU,
|
|
|
+ .lru_counter = UINT64_MAX,
|
|
|
+ .fd = -1
|
|
|
+ };
|
|
|
+ httpd_sess_enum(hd, enum_function, &context);
|
|
|
+ if (!context.session) {
|
|
|
+ return ESP_OK;
|
|
|
}
|
|
|
- ESP_LOGD(TAG, LOG_FMT("fd = %d"), lru_fd);
|
|
|
- struct sock_db *sd = httpd_sess_get(hd, lru_fd);
|
|
|
- sd->lru_socket = true;
|
|
|
- return httpd_sess_trigger_close(hd, lru_fd);
|
|
|
+ ESP_LOGD(TAG, LOG_FMT("Closing session with fd %d"), context.session->fd);
|
|
|
+ context.session->lru_socket = true;
|
|
|
+ return httpd_sess_trigger_close_(hd, context.session);
|
|
|
}
|
|
|
|
|
|
-int httpd_sess_iterate(struct httpd_data *hd, int start_fd)
|
|
|
+esp_err_t httpd_sess_trigger_close_(httpd_handle_t handle, struct sock_db *session)
|
|
|
{
|
|
|
- int start_index = 0;
|
|
|
- int i;
|
|
|
-
|
|
|
- if (start_fd != -1) {
|
|
|
- /* Take our index to where this fd is stored */
|
|
|
- for (i = 0; i < hd->config.max_open_sockets; i++) {
|
|
|
- if (hd->hd_sd[i].fd == start_fd) {
|
|
|
- start_index = i + 1;
|
|
|
- break;
|
|
|
- }
|
|
|
- }
|
|
|
+ if (!session) {
|
|
|
+ return ESP_ERR_NOT_FOUND;
|
|
|
}
|
|
|
-
|
|
|
- for (i = start_index; i < hd->config.max_open_sockets; i++) {
|
|
|
- if (hd->hd_sd[i].fd != -1) {
|
|
|
- return hd->hd_sd[i].fd;
|
|
|
- }
|
|
|
- }
|
|
|
- return -1;
|
|
|
+ return httpd_queue_work(handle, httpd_sess_close, session);
|
|
|
}
|
|
|
|
|
|
-static void httpd_sess_close(void *arg)
|
|
|
+esp_err_t httpd_sess_trigger_close(httpd_handle_t handle, int sockfd)
|
|
|
{
|
|
|
- struct sock_db *sock_db = (struct sock_db *)arg;
|
|
|
- if (sock_db) {
|
|
|
- if (sock_db->lru_counter == 0 && !sock_db->lru_socket) {
|
|
|
- ESP_LOGD(TAG, "Skipping session close for %d as it seems to be a race condition", sock_db->fd);
|
|
|
- return;
|
|
|
- }
|
|
|
- int fd = sock_db->fd;
|
|
|
- sock_db->lru_socket = false;
|
|
|
- struct httpd_data *hd = (struct httpd_data *) sock_db->handle;
|
|
|
- httpd_sess_delete(hd, fd);
|
|
|
- close(fd);
|
|
|
+ struct sock_db *session = httpd_sess_get(handle, sockfd);
|
|
|
+ if (!session) {
|
|
|
+ return ESP_ERR_NOT_FOUND;
|
|
|
}
|
|
|
+ return httpd_sess_trigger_close_(handle, session);
|
|
|
}
|
|
|
|
|
|
-esp_err_t httpd_sess_trigger_close(httpd_handle_t handle, int sockfd)
|
|
|
+void httpd_sess_close_all(struct httpd_data *hd)
|
|
|
{
|
|
|
- struct sock_db *sock_db = httpd_sess_get(handle, sockfd);
|
|
|
- if (sock_db) {
|
|
|
- return httpd_queue_work(handle, httpd_sess_close, sock_db);
|
|
|
- }
|
|
|
-
|
|
|
- return ESP_ERR_NOT_FOUND;
|
|
|
+ enum_context_t context = {
|
|
|
+ .task = HTTPD_TASK_CLOSE,
|
|
|
+ .hd = hd
|
|
|
+ };
|
|
|
+ httpd_sess_enum(hd, enum_function, &context);
|
|
|
}
|