1 // Copyright 2018 Espressif Systems (Shanghai) PTE LTD
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
7 // http://www.apache.org/licenses/LICENSE-2.0
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
20 #include <http_server.h>
21 #include "httpd_priv.h"
23 static const char *TAG = "httpd_sess";
25 bool httpd_is_sess_available(struct httpd_data *hd)
28 for (i = 0; i < hd->config.max_open_sockets; i++) {
29 if (hd->hd_sd[i].fd == -1) {
36 static struct sock_db *httpd_sess_get(struct httpd_data *hd, int newfd)
39 for (i = 0; i < hd->config.max_open_sockets; i++) {
40 if (hd->hd_sd[i].fd == newfd) {
47 esp_err_t httpd_sess_new(struct httpd_data *hd, int newfd)
49 ESP_LOGD(TAG, LOG_FMT("fd = %d"), newfd);
51 if (httpd_sess_get(hd, newfd)) {
52 ESP_LOGE(TAG, LOG_FMT("session already exists with fd = %d"), newfd);
57 for (i = 0; i < hd->config.max_open_sockets; i++) {
58 if (hd->hd_sd[i].fd == -1) {
59 memset(&hd->hd_sd[i], 0, sizeof(hd->hd_sd[i]));
60 hd->hd_sd[i].fd = newfd;
61 hd->hd_sd[i].handle = (httpd_handle_t) hd;
62 hd->hd_sd[i].send_fn = httpd_default_send;
63 hd->hd_sd[i].recv_fn = httpd_default_recv;
67 ESP_LOGD(TAG, LOG_FMT("unable to launch session for fd = %d"), newfd);
71 void *httpd_sess_get_ctx(httpd_handle_t handle, int sockfd)
77 struct httpd_data *hd = (struct httpd_data *) handle;
78 struct sock_db *sd = httpd_sess_get(hd, sockfd);
86 void httpd_sess_set_descriptors(struct httpd_data *hd,
87 fd_set *fdset, int *maxfd)
91 for (i = 0; i < hd->config.max_open_sockets; i++) {
92 if (hd->hd_sd[i].fd != -1) {
93 FD_SET(hd->hd_sd[i].fd, fdset);
94 if (hd->hd_sd[i].fd > *maxfd) {
95 *maxfd = hd->hd_sd[i].fd;
101 int httpd_sess_delete(struct httpd_data *hd, int fd)
103 ESP_LOGD(TAG, LOG_FMT("fd = %d"), fd);
105 int pre_sess_fd = -1;
106 for (i = 0; i < hd->config.max_open_sockets; i++) {
107 if (hd->hd_sd[i].fd == fd) {
108 hd->hd_sd[i].fd = -1;
109 if (hd->hd_sd[i].ctx) {
110 if (hd->hd_sd[i].free_ctx) {
111 hd->hd_sd[i].free_ctx(hd->hd_sd[i].ctx);
113 free(hd->hd_sd[i].ctx);
115 hd->hd_sd[i].ctx = NULL;
116 hd->hd_sd[i].free_ctx = NULL;
119 } else if (hd->hd_sd[i].fd != -1) {
120 /* Return the fd just preceding the one being
121 * deleted so that iterator can continue from
123 pre_sess_fd = hd->hd_sd[i].fd;
129 void httpd_sess_init(struct httpd_data *hd)
132 for (i = 0; i < hd->config.max_open_sockets; i++) {
133 hd->hd_sd[i].fd = -1;
134 hd->hd_sd[i].ctx = NULL;
138 bool httpd_sess_pending(struct httpd_data *hd, int fd)
140 struct sock_db *sd = httpd_sess_get(hd, fd);
145 return (sd->pending_len != 0);
148 /* This MUST return ESP_OK on successful execution. If any other
149 * value is returned, everything related to this socket will be
150 * cleaned up and the socket will be closed.
152 esp_err_t httpd_sess_process(struct httpd_data *hd, int newfd)
154 struct sock_db *sd = httpd_sess_get(hd, newfd);
159 ESP_LOGD(TAG, LOG_FMT("httpd_req_new"));
160 if (httpd_req_new(hd, sd) != ESP_OK) {
163 ESP_LOGD(TAG, LOG_FMT("httpd_req_delete"));
164 if (httpd_req_delete(hd) != ESP_OK) {
167 ESP_LOGD(TAG, LOG_FMT("success"));
168 sd->timestamp = httpd_os_get_timestamp();
172 esp_err_t httpd_sess_update_timestamp(httpd_handle_t handle, int sockfd)
174 if (handle == NULL) {
175 return ESP_ERR_INVALID_ARG;
178 /* Search for the socket database entry */
179 struct httpd_data *hd = (struct httpd_data *) handle;
181 for (i = 0; i < hd->config.max_open_sockets; i++) {
182 if (hd->hd_sd[i].fd == sockfd) {
183 hd->hd_sd[i].timestamp = httpd_os_get_timestamp();
187 return ESP_ERR_NOT_FOUND;
190 esp_err_t httpd_sess_close_lru(struct httpd_data *hd)
192 int64_t timestamp = INT64_MAX;
195 for (i = 0; i < hd->config.max_open_sockets; i++) {
196 /* If a descriptor is -1, there is no need to close any session.
197 * So, we can return from here, without finding the Least Recently Used
200 if (hd->hd_sd[i].fd == -1) {
203 if (hd->hd_sd[i].timestamp < timestamp) {
204 timestamp = hd->hd_sd[i].timestamp;
205 lru_fd = hd->hd_sd[i].fd;
208 ESP_LOGD(TAG, LOG_FMT("fd = %d"), lru_fd);
209 return httpd_trigger_sess_close(hd, lru_fd);
212 int httpd_sess_iterate(struct httpd_data *hd, int start_fd)
217 if (start_fd != -1) {
218 /* Take our index to where this fd is stored */
219 for (i = 0; i < hd->config.max_open_sockets; i++) {
220 if (hd->hd_sd[i].fd == start_fd) {
227 for (i = start_index; i < hd->config.max_open_sockets; i++) {
228 if (hd->hd_sd[i].fd != -1) {
229 return hd->hd_sd[i].fd;
235 static void httpd_sess_close(void *arg)
237 struct sock_db *sock_db = (struct sock_db *)arg;
239 int fd = sock_db->fd;
240 struct httpd_data *hd = (struct httpd_data *) sock_db->handle;
241 httpd_sess_delete(hd, fd);
246 esp_err_t httpd_trigger_sess_close(httpd_handle_t handle, int sockfd)
248 if (handle == NULL) {
249 return ESP_ERR_INVALID_ARG;
252 struct httpd_data *hd = (struct httpd_data *) handle;
253 struct sock_db *sock_db = httpd_sess_get(hd, sockfd);
255 return httpd_queue_work(handle, httpd_sess_close, sock_db);
258 return ESP_ERR_NOT_FOUND;