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