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 <esp_http_server.h>
21 #include "esp_httpd_priv.h"
22
23 static const char *TAG = "httpd_sess";
24
httpd_is_sess_available(struct httpd_data * hd)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
httpd_sess_get(struct httpd_data * hd,int sockfd)36 struct sock_db *httpd_sess_get(struct httpd_data *hd, int sockfd)
37 {
38 if (hd == NULL) {
39 return NULL;
40 }
41
42 /* Check if called inside a request handler, and the
43 * session sockfd in use is same as the parameter */
44 if ((hd->hd_req_aux.sd) && (hd->hd_req_aux.sd->fd == sockfd)) {
45 /* Just return the pointer to the sock_db
46 * corresponding to the request */
47 return hd->hd_req_aux.sd;
48 }
49
50 int i;
51 for (i = 0; i < hd->config.max_open_sockets; i++) {
52 if (hd->hd_sd[i].fd == sockfd) {
53 return &hd->hd_sd[i];
54 }
55 }
56 return NULL;
57 }
58
httpd_sess_new(struct httpd_data * hd,int newfd)59 esp_err_t httpd_sess_new(struct httpd_data *hd, int newfd)
60 {
61 ESP_LOGD(TAG, LOG_FMT("fd = %d"), newfd);
62
63 if (httpd_sess_get(hd, newfd)) {
64 ESP_LOGE(TAG, LOG_FMT("session already exists with fd = %d"), newfd);
65 return ESP_FAIL;
66 }
67
68 int i;
69 for (i = 0; i < hd->config.max_open_sockets; i++) {
70 if (hd->hd_sd[i].fd == -1) {
71 memset(&hd->hd_sd[i], 0, sizeof(hd->hd_sd[i]));
72 hd->hd_sd[i].fd = newfd;
73 hd->hd_sd[i].handle = (httpd_handle_t) hd;
74 hd->hd_sd[i].send_fn = httpd_default_send;
75 hd->hd_sd[i].recv_fn = httpd_default_recv;
76
77 /* Call user-defined session opening function */
78 if (hd->config.open_fn) {
79 esp_err_t ret = hd->config.open_fn(hd, hd->hd_sd[i].fd);
80 if (ret != ESP_OK) {
81 httpd_sess_delete(hd, hd->hd_sd[i].fd);
82 ESP_LOGD(TAG, LOG_FMT("open_fn failed for fd = %d"), newfd);
83 return ret;
84 }
85 }
86 return ESP_OK;
87 }
88 }
89 ESP_LOGD(TAG, LOG_FMT("unable to launch session for fd = %d"), newfd);
90 return ESP_FAIL;
91 }
92
httpd_sess_free_ctx(void * ctx,httpd_free_ctx_fn_t free_fn)93 void httpd_sess_free_ctx(void *ctx, httpd_free_ctx_fn_t free_fn)
94 {
95 if (ctx) {
96 if (free_fn) {
97 free_fn(ctx);
98 } else {
99 free(ctx);
100 }
101 }
102 }
103
httpd_sess_get_ctx(httpd_handle_t handle,int sockfd)104 void *httpd_sess_get_ctx(httpd_handle_t handle, int sockfd)
105 {
106 struct sock_db *sd = httpd_sess_get(handle, sockfd);
107 if (sd == NULL) {
108 return NULL;
109 }
110
111 /* Check if the function has been called from inside a
112 * request handler, in which case fetch the context from
113 * the httpd_req_t structure */
114 struct httpd_data *hd = (struct httpd_data *) handle;
115 if (hd->hd_req_aux.sd == sd) {
116 return hd->hd_req.sess_ctx;
117 }
118
119 return sd->ctx;
120 }
121
httpd_sess_set_ctx(httpd_handle_t handle,int sockfd,void * ctx,httpd_free_ctx_fn_t free_fn)122 void httpd_sess_set_ctx(httpd_handle_t handle, int sockfd, void *ctx, httpd_free_ctx_fn_t free_fn)
123 {
124 struct sock_db *sd = httpd_sess_get(handle, sockfd);
125 if (sd == NULL) {
126 return;
127 }
128
129 /* Check if the function has been called from inside a
130 * request handler, in which case set the context inside
131 * the httpd_req_t structure */
132 struct httpd_data *hd = (struct httpd_data *) handle;
133 if (hd->hd_req_aux.sd == sd) {
134 if (hd->hd_req.sess_ctx != ctx) {
135 /* Don't free previous context if it is in sockdb
136 * as it will be freed inside httpd_req_cleanup() */
137 if (sd->ctx != hd->hd_req.sess_ctx) {
138 /* Free previous context */
139 httpd_sess_free_ctx(hd->hd_req.sess_ctx, hd->hd_req.free_ctx);
140 }
141 hd->hd_req.sess_ctx = ctx;
142 }
143 hd->hd_req.free_ctx = free_fn;
144 return;
145 }
146
147 /* Else set the context inside the sock_db structure */
148 if (sd->ctx != ctx) {
149 /* Free previous context */
150 httpd_sess_free_ctx(sd->ctx, sd->free_ctx);
151 sd->ctx = ctx;
152 }
153 sd->free_ctx = free_fn;
154 }
155
httpd_sess_get_transport_ctx(httpd_handle_t handle,int sockfd)156 void *httpd_sess_get_transport_ctx(httpd_handle_t handle, int sockfd)
157 {
158 struct sock_db *sd = httpd_sess_get(handle, sockfd);
159 if (sd == NULL) {
160 return NULL;
161 }
162
163 return sd->transport_ctx;
164 }
165
httpd_sess_set_transport_ctx(httpd_handle_t handle,int sockfd,void * ctx,httpd_free_ctx_fn_t free_fn)166 void httpd_sess_set_transport_ctx(httpd_handle_t handle, int sockfd, void *ctx, httpd_free_ctx_fn_t free_fn)
167 {
168 struct sock_db *sd = httpd_sess_get(handle, sockfd);
169 if (sd == NULL) {
170 return;
171 }
172
173 if (sd->transport_ctx != ctx) {
174 /* Free previous transport context */
175 httpd_sess_free_ctx(sd->transport_ctx, sd->free_transport_ctx);
176 sd->transport_ctx = ctx;
177 }
178 sd->free_transport_ctx = free_fn;
179 }
180
httpd_sess_set_descriptors(struct httpd_data * hd,fd_set * fdset,int * maxfd)181 void httpd_sess_set_descriptors(struct httpd_data *hd,
182 fd_set *fdset, int *maxfd)
183 {
184 int i;
185 *maxfd = -1;
186 for (i = 0; i < hd->config.max_open_sockets; i++) {
187 if (hd->hd_sd[i].fd != -1) {
188 FD_SET(hd->hd_sd[i].fd, fdset);
189 if (hd->hd_sd[i].fd > *maxfd) {
190 *maxfd = hd->hd_sd[i].fd;
191 }
192 }
193 }
194 }
195
196 /** Check if a FD is valid */
fd_is_valid(int fd)197 static int fd_is_valid(int fd)
198 {
199 return fcntl(fd, F_GETFD) != -1 || errno != EBADF;
200 }
201
httpd_sess_get_lru_counter(void)202 static inline uint64_t httpd_sess_get_lru_counter(void)
203 {
204 static uint64_t lru_counter = 0;
205 return ++lru_counter;
206 }
207
httpd_sess_delete_invalid(struct httpd_data * hd)208 void httpd_sess_delete_invalid(struct httpd_data *hd)
209 {
210 for (int i = 0; i < hd->config.max_open_sockets; i++) {
211 if (hd->hd_sd[i].fd != -1 && !fd_is_valid(hd->hd_sd[i].fd)) {
212 ESP_LOGW(TAG, LOG_FMT("Closing invalid socket %d"), hd->hd_sd[i].fd);
213 httpd_sess_delete(hd, hd->hd_sd[i].fd);
214 }
215 }
216 }
217
httpd_sess_delete(struct httpd_data * hd,int fd)218 int httpd_sess_delete(struct httpd_data *hd, int fd)
219 {
220 ESP_LOGD(TAG, LOG_FMT("fd = %d"), fd);
221 int i;
222 int pre_sess_fd = -1;
223 for (i = 0; i < hd->config.max_open_sockets; i++) {
224 if (hd->hd_sd[i].fd == fd) {
225 /* global close handler */
226 if (hd->config.close_fn) {
227 hd->config.close_fn(hd, fd);
228 }
229
230 /* release 'user' context */
231 if (hd->hd_sd[i].ctx) {
232 if (hd->hd_sd[i].free_ctx) {
233 hd->hd_sd[i].free_ctx(hd->hd_sd[i].ctx);
234 } else {
235 free(hd->hd_sd[i].ctx);
236 }
237 hd->hd_sd[i].ctx = NULL;
238 hd->hd_sd[i].free_ctx = NULL;
239 }
240
241 /* release 'transport' context */
242 if (hd->hd_sd[i].transport_ctx) {
243 if (hd->hd_sd[i].free_transport_ctx) {
244 hd->hd_sd[i].free_transport_ctx(hd->hd_sd[i].transport_ctx);
245 } else {
246 free(hd->hd_sd[i].transport_ctx);
247 }
248 hd->hd_sd[i].transport_ctx = NULL;
249 hd->hd_sd[i].free_transport_ctx = NULL;
250 }
251
252 /* mark session slot as available */
253 hd->hd_sd[i].fd = -1;
254 break;
255 } else if (hd->hd_sd[i].fd != -1) {
256 /* Return the fd just preceding the one being
257 * deleted so that iterator can continue from
258 * the correct fd */
259 pre_sess_fd = hd->hd_sd[i].fd;
260 }
261 }
262 return pre_sess_fd;
263 }
264
httpd_sess_init(struct httpd_data * hd)265 void httpd_sess_init(struct httpd_data *hd)
266 {
267 int i;
268 for (i = 0; i < hd->config.max_open_sockets; i++) {
269 hd->hd_sd[i].fd = -1;
270 hd->hd_sd[i].ctx = NULL;
271 }
272 }
273
httpd_sess_pending(struct httpd_data * hd,int fd)274 bool httpd_sess_pending(struct httpd_data *hd, int fd)
275 {
276 struct sock_db *sd = httpd_sess_get(hd, fd);
277 if (! sd) {
278 return ESP_FAIL;
279 }
280
281 if (sd->pending_fn) {
282 // test if there's any data to be read (besides read() function, which is handled by select() in the main httpd loop)
283 // this should check e.g. for the SSL data buffer
284 if (sd->pending_fn(hd, fd) > 0) {
285 return true;
286 }
287 }
288
289 return (sd->pending_len != 0);
290 }
291
292 /* This MUST return ESP_OK on successful execution. If any other
293 * value is returned, everything related to this socket will be
294 * cleaned up and the socket will be closed.
295 */
httpd_sess_process(struct httpd_data * hd,int newfd)296 esp_err_t httpd_sess_process(struct httpd_data *hd, int newfd)
297 {
298 struct sock_db *sd = httpd_sess_get(hd, newfd);
299 if (! sd) {
300 return ESP_FAIL;
301 }
302
303 ESP_LOGD(TAG, LOG_FMT("httpd_req_new"));
304 if (httpd_req_new(hd, sd) != ESP_OK) {
305 return ESP_FAIL;
306 }
307 ESP_LOGD(TAG, LOG_FMT("httpd_req_delete"));
308 if (httpd_req_delete(hd) != ESP_OK) {
309 return ESP_FAIL;
310 }
311 ESP_LOGD(TAG, LOG_FMT("success"));
312 sd->lru_counter = httpd_sess_get_lru_counter();
313 return ESP_OK;
314 }
315
httpd_sess_update_lru_counter(httpd_handle_t handle,int sockfd)316 esp_err_t httpd_sess_update_lru_counter(httpd_handle_t handle, int sockfd)
317 {
318 if (handle == NULL) {
319 return ESP_ERR_INVALID_ARG;
320 }
321
322 /* Search for the socket database entry */
323 struct httpd_data *hd = (struct httpd_data *) handle;
324 int i;
325 for (i = 0; i < hd->config.max_open_sockets; i++) {
326 if (hd->hd_sd[i].fd == sockfd) {
327 hd->hd_sd[i].lru_counter = httpd_sess_get_lru_counter();
328 return ESP_OK;
329 }
330 }
331 return ESP_ERR_NOT_FOUND;
332 }
333
httpd_sess_close_lru(struct httpd_data * hd)334 esp_err_t httpd_sess_close_lru(struct httpd_data *hd)
335 {
336 uint64_t lru_counter = UINT64_MAX;
337 int lru_fd = -1;
338 int i;
339 for (i = 0; i < hd->config.max_open_sockets; i++) {
340 /* If a descriptor is -1, there is no need to close any session.
341 * So, we can return from here, without finding the Least Recently Used
342 * session
343 */
344 if (hd->hd_sd[i].fd == -1) {
345 return ESP_OK;
346 }
347 if (hd->hd_sd[i].lru_counter < lru_counter) {
348 lru_counter = hd->hd_sd[i].lru_counter;
349 lru_fd = hd->hd_sd[i].fd;
350 }
351 }
352 ESP_LOGD(TAG, LOG_FMT("fd = %d"), lru_fd);
353 struct sock_db *sd = httpd_sess_get(hd, lru_fd);
354 sd->lru_socket = true;
355 return httpd_sess_trigger_close(hd, lru_fd);
356 }
357
httpd_sess_iterate(struct httpd_data * hd,int start_fd)358 int httpd_sess_iterate(struct httpd_data *hd, int start_fd)
359 {
360 int start_index = 0;
361 int i;
362
363 if (start_fd != -1) {
364 /* Take our index to where this fd is stored */
365 for (i = 0; i < hd->config.max_open_sockets; i++) {
366 if (hd->hd_sd[i].fd == start_fd) {
367 start_index = i + 1;
368 break;
369 }
370 }
371 }
372
373 for (i = start_index; i < hd->config.max_open_sockets; i++) {
374 if (hd->hd_sd[i].fd != -1) {
375 return hd->hd_sd[i].fd;
376 }
377 }
378 return -1;
379 }
380
httpd_sess_close(void * arg)381 static void httpd_sess_close(void *arg)
382 {
383 struct sock_db *sock_db = (struct sock_db *)arg;
384 if (sock_db) {
385 if (sock_db->lru_counter == 0 && !sock_db->lru_socket) {
386 ESP_LOGD(TAG, "Skipping session close for %d as it seems to be a race condition", sock_db->fd);
387 return;
388 }
389 int fd = sock_db->fd;
390 sock_db->lru_socket = false;
391 struct httpd_data *hd = (struct httpd_data *) sock_db->handle;
392 httpd_sess_delete(hd, fd);
393 close(fd);
394 }
395 }
396
httpd_sess_trigger_close(httpd_handle_t handle,int sockfd)397 esp_err_t httpd_sess_trigger_close(httpd_handle_t handle, int sockfd)
398 {
399 struct sock_db *sock_db = httpd_sess_get(handle, sockfd);
400 if (sock_db) {
401 return httpd_queue_work(handle, httpd_sess_close, sock_db);
402 }
403
404 return ESP_ERR_NOT_FOUND;
405 }
406