1 /*
2  * SPDX-FileCopyrightText: 2018-2021 Espressif Systems (Shanghai) CO LTD
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 
8 #ifndef _HTTPD_PRIV_H_
9 #define _HTTPD_PRIV_H_
10 
11 #include <stdbool.h>
12 #include <sys/socket.h>
13 #include <sys/param.h>
14 #include <netinet/in.h>
15 #include <esp_log.h>
16 #include <esp_err.h>
17 
18 #include <esp_http_server.h>
19 #include "osal.h"
20 
21 #ifdef __cplusplus
22 extern "C" {
23 #endif
24 
25 /* Size of request data block/chunk (not to be confused with chunked encoded data)
26  * that is received and parsed in one turn of the parsing process. This should not
27  * exceed the scratch buffer size and should at least be 8 bytes */
28 #define PARSER_BLOCK_SIZE  128
29 
30 /* Calculate the maximum size needed for the scratch buffer */
31 #define HTTPD_SCRATCH_BUF  MAX(HTTPD_MAX_REQ_HDR_LEN, HTTPD_MAX_URI_LEN)
32 
33 /* Formats a log string to prepend context function name */
34 #define LOG_FMT(x)      "%s: " x, __func__
35 
36 /**
37  * @brief Thread related data for internal use
38  */
39 struct thread_data {
40     othread_t handle;   /*!< Handle to thread/task */
41     enum {
42         THREAD_IDLE = 0,
43         THREAD_RUNNING,
44         THREAD_STOPPING,
45         THREAD_STOPPED,
46     } status;           /*!< State of the thread */
47 };
48 
49 /**
50  * @brief A database of all the open sockets in the system.
51  */
52 struct sock_db {
53     int fd;                                 /*!< The file descriptor for this socket */
54     void *ctx;                              /*!< A custom context for this socket */
55     bool ignore_sess_ctx_changes;           /*!< Flag indicating if session context changes should be ignored */
56     void *transport_ctx;                    /*!< A custom 'transport' context for this socket, to be used by send/recv/pending */
57     httpd_handle_t handle;                  /*!< Server handle */
58     httpd_free_ctx_fn_t free_ctx;      /*!< Function for freeing the context */
59     httpd_free_ctx_fn_t free_transport_ctx; /*!< Function for freeing the 'transport' context */
60     httpd_send_func_t send_fn;              /*!< Send function for this socket */
61     httpd_recv_func_t recv_fn;              /*!< Receive function for this socket */
62     httpd_pending_func_t pending_fn;        /*!< Pending function for this socket */
63     uint64_t lru_counter;                   /*!< LRU Counter indicating when the socket was last used */
64     bool lru_socket;                        /*!< Flag indicating LRU socket */
65     char pending_data[PARSER_BLOCK_SIZE];   /*!< Buffer for pending data to be received */
66     size_t pending_len;                     /*!< Length of pending data to be received */
67 #ifdef CONFIG_HTTPD_WS_SUPPORT
68     bool ws_handshake_done;                 /*!< True if it has done WebSocket handshake (if this socket is a valid WS) */
69     bool ws_close;                          /*!< Set to true to close the socket later (when WS Close frame received) */
70     esp_err_t (*ws_handler)(httpd_req_t *r);   /*!< WebSocket handler, leave to null if it's not WebSocket */
71     bool ws_control_frames;                         /*!< WebSocket flag indicating that control frames should be passed to user handlers */
72     void *ws_user_ctx;                         /*!< Pointer to user context data which will be available to handler for websocket*/
73 #endif
74 };
75 
76 /**
77  * @brief   Auxiliary data structure for use during reception and processing
78  *          of requests and temporarily keeping responses
79  */
80 struct httpd_req_aux {
81     struct sock_db *sd;                             /*!< Pointer to socket database */
82     char            scratch[HTTPD_SCRATCH_BUF + 1]; /*!< Temporary buffer for our operations (1 byte extra for null termination) */
83     size_t          remaining_len;                  /*!< Amount of data remaining to be fetched */
84     char           *status;                         /*!< HTTP response's status code */
85     char           *content_type;                   /*!< HTTP response's content type */
86     bool            first_chunk_sent;               /*!< Used to indicate if first chunk sent */
87     unsigned        req_hdrs_count;                 /*!< Count of total headers in request packet */
88     unsigned        resp_hdrs_count;                /*!< Count of additional headers in response packet */
89     struct resp_hdr {
90         const char *field;
91         const char *value;
92     } *resp_hdrs;                                   /*!< Additional headers in response packet */
93     struct http_parser_url url_parse_res;           /*!< URL parsing result, used for retrieving URL elements */
94 #ifdef CONFIG_HTTPD_WS_SUPPORT
95     bool ws_handshake_detect;                       /*!< WebSocket handshake detection flag */
96     httpd_ws_type_t ws_type;                        /*!< WebSocket frame type */
97     bool ws_final;                                  /*!< WebSocket FIN bit (final frame or not) */
98     uint8_t mask_key[4];                            /*!< WebSocket mask key for this payload */
99 #endif
100 };
101 
102 /**
103  * @brief   Server data for each instance. This is exposed publicly as
104  *          httpd_handle_t but internal structure/members are kept private.
105  */
106 struct httpd_data {
107     httpd_config_t config;                  /*!< HTTPD server configuration */
108     int listen_fd;                          /*!< Server listener FD */
109     int ctrl_fd;                            /*!< Ctrl message receiver FD */
110     int msg_fd;                             /*!< Ctrl message sender FD */
111     struct thread_data hd_td;               /*!< Information for the HTTPD thread */
112     struct sock_db *hd_sd;                  /*!< The socket database */
113     int hd_sd_active_count;                 /*!< The number of the active sockets */
114     httpd_uri_t **hd_calls;                 /*!< Registered URI handlers */
115     struct httpd_req hd_req;                /*!< The current HTTPD request */
116     struct httpd_req_aux hd_req_aux;        /*!< Additional data about the HTTPD request kept unexposed */
117     uint64_t lru_counter;                   /*!< LRU counter */
118 
119     /* Array of registered error handler functions */
120     httpd_err_handler_func_t *err_handler_fns;
121 };
122 
123 /******************* Group : Session Management ********************/
124 /** @name Session Management
125  * Functions related to HTTP session management
126  * @{
127  */
128 
129 // Enum function, which will be called for each session
130 typedef int (*httpd_session_enum_function)(struct sock_db *session, void *context);
131 
132 /**
133  * @brief  Enumerates all sessions
134  *
135  * @param[in] hd            Server instance data
136  * @param[in] enum_function Enumeration function, which will be called for each session
137  * @param[in] context       Context, which will be passed to the enumeration function
138  */
139 void httpd_sess_enum(struct httpd_data *hd, httpd_session_enum_function enum_function, void *context);
140 
141 /**
142  * @brief   Returns next free session slot (fd<0)
143  *
144  * @param[in] hd    Server instance data
145  *
146  * @return
147  *  - +VE : Free session slot
148  *  - NULL: End of iteration
149  */
150 struct sock_db *httpd_sess_get_free(struct httpd_data *hd);
151 
152 /**
153  * @brief Retrieve a session by its descriptor
154  *
155  * @param[in] hd     Server instance data
156  * @param[in] sockfd Socket FD
157  * @return pointer into the socket DB, or NULL if not found
158  */
159 struct sock_db *httpd_sess_get(struct httpd_data *hd, int sockfd);
160 
161 /**
162  * @brief Delete sessions whose FDs have became invalid.
163  *        This is a recovery strategy e.g. after select() fails.
164  *
165  * @param[in] hd    Server instance data
166  */
167 void httpd_sess_delete_invalid(struct httpd_data *hd);
168 
169 /**
170  * @brief   Initializes an http session by resetting the sockets database.
171  *
172  * @param[in] hd    Server instance data
173  */
174 void httpd_sess_init(struct httpd_data *hd);
175 
176 /**
177  * @brief   Starts a new session for client requesting connection and adds
178  *          it's descriptor to the socket database.
179  *
180  * @param[in] hd    Server instance data
181  * @param[in] newfd Descriptor of the new client to be added to the session.
182  *
183  * @return
184  *  - ESP_OK   : on successfully queuing the work
185  *  - ESP_FAIL : in case of control socket error while sending
186  */
187 esp_err_t httpd_sess_new(struct httpd_data *hd, int newfd);
188 
189 /**
190  * @brief   Processes incoming HTTP requests
191  *
192  * @param[in] hd      Server instance data
193  * @param[in] session Session
194  *
195  * @return
196  *  - ESP_OK    : on successfully receiving, parsing and responding to a request
197  *  - ESP_FAIL  : in case of failure in any of the stages of processing
198  */
199 esp_err_t httpd_sess_process(struct httpd_data *hd, struct sock_db *session);
200 
201 /**
202  * @brief   Remove client descriptor from the session / socket database
203  *          and close the connection for this client.
204  *
205  * @param[in] hd      Server instance data
206  * @param[in] session Session
207  */
208 void httpd_sess_delete(struct httpd_data *hd, struct sock_db *session);
209 
210 /**
211  * @brief   Free session context
212  *
213  * @param[in] ctx     Pointer to session context
214  * @param[in] free_fn Free function to call on session context
215  */
216 void httpd_sess_free_ctx(void **ctx, httpd_free_ctx_fn_t free_fn);
217 
218 /**
219  * @brief   Add descriptors present in the socket database to an fdset and
220  *          update the value of maxfd which are needed by the select function
221  *          for looking through all available sockets for incoming data.
222  *
223  * @param[in]  hd    Server instance data
224  * @param[out] fdset File descriptor set to be updated.
225  * @param[out] maxfd Maximum value among all file descriptors.
226  */
227 void httpd_sess_set_descriptors(struct httpd_data *hd, fd_set *fdset, int *maxfd);
228 
229 /**
230  * @brief   Checks if session can accept another connection from new client.
231  *          If sockets database is full then this returns false.
232  *
233  * @param[in] hd  Server instance data
234  *
235  * @return True if session can accept new clients
236  */
237 bool httpd_is_sess_available(struct httpd_data *hd);
238 
239 /**
240  * @brief   Checks if session has any pending data/packets
241  *          for processing
242  *
243  * This is needed as httpd_unrecv may un-receive next
244  * packet in the stream. If only partial packet was
245  * received then select() would mark the fd for processing
246  * as remaining part of the packet would still be in socket
247  * recv queue. But if a complete packet got unreceived
248  * then it would not be processed until further data is
249  * received on the socket. This is when this function
250  * comes in use, as it checks the socket's pending data
251  * buffer.
252  *
253  * @param[in] hd      Server instance data
254  * @param[in] session Session
255  *
256  * @return True if there is any pending data
257  */
258 bool httpd_sess_pending(struct httpd_data *hd, struct sock_db *session);
259 
260 /**
261  * @brief   Removes the least recently used client from the session
262  *
263  * This may be useful if new clients are requesting for connection but
264  * max number of connections is reached, in which case the client which
265  * is inactive for the longest will be removed from the session.
266  *
267  * @param[in] hd  Server instance data
268  *
269  * @return
270  *  - ESP_OK    : if session closure initiated successfully
271  *  - ESP_FAIL  : if failed
272  */
273 esp_err_t httpd_sess_close_lru(struct httpd_data *hd);
274 
275 /**
276  * @brief   Closes all sessions
277  *
278  * @param[in] hd  Server instance data
279  *
280  */
281 void httpd_sess_close_all(struct httpd_data *hd);
282 
283 /** End of Group : Session Management
284  * @}
285  */
286 
287 /****************** Group : URI Handling ********************/
288 /** @name URI Handling
289  * Methods for accessing URI handlers
290  * @{
291  */
292 
293 /**
294  * @brief   For an HTTP request, searches through all the registered URI handlers
295  *          and invokes the appropriate one if found
296  *
297  * @param[in] hd  Server instance data for which handler needs to be invoked
298  *
299  * @return
300  *  - ESP_OK    : if handler found and executed successfully
301  *  - ESP_FAIL  : otherwise
302  */
303 esp_err_t httpd_uri(struct httpd_data *hd);
304 
305 /**
306  * @brief   Unregister all URI handlers
307  *
308  * @param[in] hd  Server instance data
309  */
310 void httpd_unregister_all_uri_handlers(struct httpd_data *hd);
311 
312 /**
313  * @brief   Validates the request to prevent users from calling APIs, that are to
314  *          be called only inside a URI handler, outside the handler context
315  *
316  * @param[in] req Pointer to HTTP request that needs to be validated
317  *
318  * @return
319  *  - true  : if valid request
320  *  - false : otherwise
321  */
322 bool httpd_validate_req_ptr(httpd_req_t *r);
323 
324 /* httpd_validate_req_ptr() adds some overhead to frequently used APIs,
325  * and is useful mostly for debugging, so it's preferable to disable
326  * the check by default and enable it only if necessary */
327 #ifdef CONFIG_HTTPD_VALIDATE_REQ
328 #define httpd_valid_req(r)  httpd_validate_req_ptr(r)
329 #else
330 #define httpd_valid_req(r)  true
331 #endif
332 
333 /** End of Group : URI Handling
334  * @}
335  */
336 
337 /****************** Group : Processing ********************/
338 /** @name Processing
339  * Methods for processing HTTP requests
340  * @{
341  */
342 
343 /**
344  * @brief   Initiates the processing of HTTP request
345  *
346  * Receives incoming TCP packet on a socket, then parses the packet as
347  * HTTP request and fills httpd_req_t data structure with the extracted
348  * URI, headers are ready to be fetched from scratch buffer and calling
349  * http_recv() after this reads the body of the request.
350  *
351  * @param[in] hd  Server instance data
352  * @param[in] sd  Pointer to socket which is needed for receiving TCP packets.
353  *
354  * @return
355  *  - ESP_OK    : if request packet is valid
356  *  - ESP_FAIL  : otherwise
357  */
358 esp_err_t httpd_req_new(struct httpd_data *hd, struct sock_db *sd);
359 
360 /**
361  * @brief   For an HTTP request, resets the resources allocated for it and
362  *          purges any data left to be received
363  *
364  * @param[in] hd  Server instance data
365  *
366  * @return
367  *  - ESP_OK    : if request packet deleted and resources cleaned.
368  *  - ESP_FAIL  : otherwise.
369  */
370 esp_err_t httpd_req_delete(struct httpd_data *hd);
371 
372 /**
373  * @brief   For handling HTTP errors by invoking registered
374  *          error handler function
375  *
376  * @param[in] req     Pointer to the HTTP request for which error occurred
377  * @param[in] error   Error type
378  *
379  * @return
380  *  - ESP_OK    : error handled successful
381  *  - ESP_FAIL  : failure indicates that the underlying socket needs to be closed
382  */
383 esp_err_t httpd_req_handle_err(httpd_req_t *req, httpd_err_code_t error);
384 
385 /** End of Group : Parsing
386  * @}
387  */
388 
389 /****************** Group : Send/Receive ********************/
390 /** @name Send and Receive
391  * Methods for transmitting and receiving HTTP requests and responses
392  * @{
393  */
394 
395 /**
396  * @brief   For sending out data in response to an HTTP request.
397  *
398  * @param[in] req     Pointer to the HTTP request for which the response needs to be sent
399  * @param[in] buf     Pointer to the buffer from where the body of the response is taken
400  * @param[in] buf_len Length of the buffer
401  *
402  * @return
403  *  - Length of data : if successful
404  *  - ESP_FAIL       : if failed
405  */
406 int httpd_send(httpd_req_t *req, const char *buf, size_t buf_len);
407 
408 /**
409  * @brief   For receiving HTTP request data
410  *
411  * @note    The exposed API httpd_recv() is simply this function with last parameter
412  *          set as false. This function is used internally during reception and
413  *          processing of a new request. The option to halt after receiving pending
414  *          data prevents the server from requesting more data than is needed for
415  *          completing a packet in case when all the remaining part of the packet is
416  *          in the pending buffer.
417  *
418  * @param[in]  req    Pointer to new HTTP request which only has the socket descriptor
419  * @param[out] buf    Pointer to the buffer which will be filled with the received data
420  * @param[in] buf_len Length of the buffer
421  * @param[in] halt_after_pending When set true, halts immediately after receiving from
422  *                               pending buffer
423  *
424  * @return
425  *  - Length of data : if successful
426  *  - ESP_FAIL       : if failed
427  */
428 int httpd_recv_with_opt(httpd_req_t *r, char *buf, size_t buf_len, bool halt_after_pending);
429 
430 /**
431  * @brief   For un-receiving HTTP request data
432  *
433  * This function copies data into internal buffer pending_data so that
434  * when httpd_recv is called, it first fetches this pending data and
435  * then only starts receiving from the socket
436  *
437  * @note    If data is too large for the internal buffer then only
438  *          part of the data is unreceived, reflected in the returned
439  *          length. Make sure that such truncation is checked for and
440  *          handled properly.
441  *
442  * @param[in] req     Pointer to new HTTP request which only has the socket descriptor
443  * @param[in] buf     Pointer to the buffer from where data needs to be un-received
444  * @param[in] buf_len Length of the buffer
445  *
446  * @return  Length of data copied into pending buffer
447  */
448 size_t httpd_unrecv(struct httpd_req *r, const char *buf, size_t buf_len);
449 
450 /**
451  * @brief   This is the low level default send function of the HTTPD. This should
452  *          NEVER be called directly. The semantics of this is exactly similar to
453  *          send() of the BSD socket API.
454  *
455  * @param[in] hd      Server instance data
456  * @param[in] sockfd  Socket descriptor for sending data
457  * @param[in] buf     Pointer to the buffer from where the body of the response is taken
458  * @param[in] buf_len Length of the buffer
459  * @param[in] flags   Flags for mode selection
460  *
461  * @return
462  *  - Length of data : if successful
463  *  - -1             : if failed (appropriate errno is set)
464  */
465 int httpd_default_send(httpd_handle_t hd, int sockfd, const char *buf, size_t buf_len, int flags);
466 
467 /**
468  * @brief   This is the low level default recv function of the HTTPD. This should
469  *          NEVER be called directly. The semantics of this is exactly similar to
470  *          recv() of the BSD socket API.
471  *
472  * @param[in] hd      Server instance data
473  * @param[in] sockfd  Socket descriptor for sending data
474  * @param[out] buf    Pointer to the buffer which will be filled with the received data
475  * @param[in] buf_len Length of the buffer
476  * @param[in] flags   Flags for mode selection
477  *
478  * @return
479  *  - Length of data : if successful
480  *  - -1             : if failed (appropriate errno is set)
481  */
482 int httpd_default_recv(httpd_handle_t hd, int sockfd, char *buf, size_t buf_len, int flags);
483 
484 /** End of Group : Send and Receive
485  * @}
486  */
487 
488 /* ************** Group: WebSocket ************** */
489 /** @name WebSocket
490  * Functions for WebSocket header parsing
491  * @{
492  */
493 
494 
495 /**
496  * @brief   This function is for responding a WebSocket handshake
497  *
498  * @param[in] req                       Pointer to handshake request that will be handled
499  * @param[in] supported_subprotocol     Pointer to the subprotocol supported by this URI
500  * @return
501  *  - ESP_OK                        : When handshake is sucessful
502  *  - ESP_ERR_NOT_FOUND             : When some headers (Sec-WebSocket-*) are not found
503  *  - ESP_ERR_INVALID_VERSION       : The WebSocket version is not "13"
504  *  - ESP_ERR_INVALID_STATE         : Handshake was done beforehand
505  *  - ESP_ERR_INVALID_ARG           : Argument is invalid (null or non-WebSocket)
506  *  - ESP_FAIL                      : Socket failures
507  */
508 esp_err_t httpd_ws_respond_server_handshake(httpd_req_t *req, const char *supported_subprotocol);
509 
510 /**
511  * @brief   This function is for getting a frame type
512  *          and responding a WebSocket control frame automatically
513  *
514  * @param[in] req    Pointer to handshake request that will be handled
515  * @return
516  *  - ESP_OK                        : When handshake is sucessful
517  *  - ESP_ERR_INVALID_ARG           : Argument is invalid (null or non-WebSocket)
518  *  - ESP_ERR_INVALID_STATE         : Received only some parts of a control frame
519  *  - ESP_FAIL                      : Socket failures
520  */
521 esp_err_t httpd_ws_get_frame_type(httpd_req_t *req);
522 
523 /**
524  * @brief   Trigger an httpd session close externally
525  *
526  * @note    Calling this API is only required in special circumstances wherein
527  *          some application requires to close an httpd client session asynchronously.
528  *
529  * @param[in] handle    Handle to server returned by httpd_start
530  * @param[in] session   Session to be closed
531  *
532  * @return
533  *  - ESP_OK    : On successfully initiating closure
534  *  - ESP_FAIL  : Failure to queue work
535  *  - ESP_ERR_NOT_FOUND   : Socket fd not found
536  *  - ESP_ERR_INVALID_ARG : Null arguments
537  */
538 esp_err_t httpd_sess_trigger_close_(httpd_handle_t handle, struct sock_db *session);
539 
540 /** End of WebSocket related functions
541  * @}
542  */
543 
544 #ifdef __cplusplus
545 }
546 #endif
547 
548 #endif /* ! _HTTPD_PRIV_H_ */
549