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