]> granicus.if.org Git - esp-idf/blob - components/http_server/src/httpd_sess.c
Http Server : Add a simple light weight HTTP Server Component.
[esp-idf] / components / http_server / src / httpd_sess.c
1 // Copyright 2018 Espressif Systems (Shanghai) PTE LTD
2 //
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
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
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.
14
15
16 #include <stdlib.h>
17 #include <esp_log.h>
18 #include <esp_err.h>
19
20 #include <http_server.h>
21 #include "httpd_priv.h"
22
23 static const char *TAG = "httpd_sess";
24
25 bool httpd_is_sess_available(struct httpd_data *hd)
26 {
27     int i;
28     for (i = 0; i < hd->config.max_open_sockets; i++) {
29         if (hd->hd_sd[i].fd == -1) {
30             return true;
31         }
32     }
33     return false;
34 }
35
36 static struct sock_db *httpd_sess_get(struct httpd_data *hd, int newfd)
37 {
38     int i;
39     for (i = 0; i < hd->config.max_open_sockets; i++) {
40         if (hd->hd_sd[i].fd == newfd) {
41             return &hd->hd_sd[i];
42         }
43     }
44     return NULL;
45 }
46
47 esp_err_t httpd_sess_new(struct httpd_data *hd, int newfd)
48 {
49     ESP_LOGD(TAG, LOG_FMT("fd = %d"), newfd);
50
51     if (httpd_sess_get(hd, newfd)) {
52         ESP_LOGE(TAG, LOG_FMT("session already exists with fd = %d"), newfd);
53         return ESP_FAIL;
54     }
55
56     int i;
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;
64             return ESP_OK;
65         }
66     }
67     ESP_LOGD(TAG, LOG_FMT("unable to launch session for fd = %d"), newfd);
68     return ESP_FAIL;
69 }
70
71 void *httpd_sess_get_ctx(httpd_handle_t handle, int sockfd)
72 {
73     if (handle == NULL) {
74         return NULL;
75     }
76
77     struct httpd_data *hd = (struct httpd_data *) handle;
78     struct sock_db    *sd = httpd_sess_get(hd, sockfd);
79     if (sd == NULL) {
80         return NULL;
81     }
82
83     return sd->ctx;
84 }
85
86 void httpd_sess_set_descriptors(struct httpd_data *hd,
87                                 fd_set *fdset, int *maxfd)
88 {
89     int i;
90     *maxfd = -1;
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;
96             }
97         }
98     }
99 }
100
101 int httpd_sess_delete(struct httpd_data *hd, int fd)
102 {
103     ESP_LOGD(TAG, LOG_FMT("fd = %d"), fd);
104     int i;
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);
112                 } else {
113                     free(hd->hd_sd[i].ctx);
114                 }
115                 hd->hd_sd[i].ctx = NULL;
116                 hd->hd_sd[i].free_ctx = NULL;
117             }
118             break;
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
122              * the correct fd */
123             pre_sess_fd = hd->hd_sd[i].fd;
124         }
125     }
126     return pre_sess_fd;
127 }
128
129 void httpd_sess_init(struct httpd_data *hd)
130 {
131     int i;
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;
135     }
136 }
137
138 bool httpd_sess_pending(struct httpd_data *hd, int fd)
139 {
140     struct sock_db *sd = httpd_sess_get(hd, fd);
141     if (! sd) {
142         return ESP_FAIL;
143     }
144
145     return (sd->pending_len != 0);
146 }
147
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.
151  */
152 esp_err_t httpd_sess_process(struct httpd_data *hd, int newfd)
153 {
154     struct sock_db *sd = httpd_sess_get(hd, newfd);
155     if (! sd) {
156         return ESP_FAIL;
157     }
158
159     ESP_LOGD(TAG, LOG_FMT("httpd_req_new"));
160     if (httpd_req_new(hd, sd) != ESP_OK) {
161         return ESP_FAIL;
162     }
163     ESP_LOGD(TAG, LOG_FMT("httpd_req_delete"));
164     if (httpd_req_delete(hd) != ESP_OK) {
165         return ESP_FAIL;
166     }
167     ESP_LOGD(TAG, LOG_FMT("success"));
168     sd->timestamp = httpd_os_get_timestamp();
169     return ESP_OK;
170 }
171
172 esp_err_t httpd_sess_update_timestamp(httpd_handle_t handle, int sockfd)
173 {
174     if (handle == NULL) {
175         return ESP_ERR_INVALID_ARG;
176     }
177
178     /* Search for the socket database entry */
179     struct httpd_data *hd = (struct httpd_data *) handle;
180     int i;
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();
184             return ESP_OK;
185         }
186     }
187     return ESP_ERR_NOT_FOUND;
188 }
189
190 esp_err_t httpd_sess_close_lru(struct httpd_data *hd)
191 {
192     int64_t timestamp = INT64_MAX;
193     int lru_fd = -1;
194     int i;
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
198          * session
199          */
200         if (hd->hd_sd[i].fd == -1) {
201             return ESP_OK;
202         }
203         if (hd->hd_sd[i].timestamp < timestamp) {
204             timestamp = hd->hd_sd[i].timestamp;
205             lru_fd = hd->hd_sd[i].fd;
206         }
207     }
208     ESP_LOGD(TAG, LOG_FMT("fd = %d"), lru_fd);
209     return httpd_trigger_sess_close(hd, lru_fd);
210 }
211
212 int httpd_sess_iterate(struct httpd_data *hd, int start_fd)
213 {
214     int start_index = 0;
215     int i;
216
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) {
221                 start_index = i + 1;
222                 break;
223             }
224         }
225     }
226
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;
230         }
231     }
232     return -1;
233 }
234
235 static void httpd_sess_close(void *arg)
236 {
237     struct sock_db *sock_db = (struct sock_db *)arg;
238     if (sock_db) {
239         int fd = sock_db->fd;
240         struct httpd_data *hd = (struct httpd_data *) sock_db->handle;
241         httpd_sess_delete(hd, fd);
242         close(fd);
243     }
244 }
245
246 esp_err_t httpd_trigger_sess_close(httpd_handle_t handle, int sockfd)
247 {
248     if (handle == NULL) {
249         return ESP_ERR_INVALID_ARG;
250     }
251
252     struct httpd_data *hd = (struct httpd_data *) handle;
253     struct sock_db *sock_db = httpd_sess_get(hd, sockfd);
254     if (sock_db) {
255         return httpd_queue_work(handle, httpd_sess_close, sock_db);
256     }
257
258     return ESP_ERR_NOT_FOUND;
259 }