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 #ifndef _ESP_HTTP_SERVER_H_
16 #define _ESP_HTTP_SERVER_H_
17 
18 #include <stdio.h>
19 #include <string.h>
20 #include <freertos/FreeRTOS.h>
21 #include <freertos/task.h>
22 #include <http_parser.h>
23 #include <sdkconfig.h>
24 #include <esp_err.h>
25 
26 #ifdef __cplusplus
27 extern "C" {
28 #endif
29 
30 /*
31 note: esp_https_server.h includes a customized copy of this
32 initializer that should be kept in sync
33 */
34 #define HTTPD_DEFAULT_CONFIG() {                        \
35         .task_priority      = tskIDLE_PRIORITY+5,       \
36         .stack_size         = 4096,                     \
37         .core_id            = tskNO_AFFINITY,           \
38         .server_port        = 80,                       \
39         .ctrl_port          = 32768,                    \
40         .max_open_sockets   = 7,                        \
41         .max_uri_handlers   = 8,                        \
42         .max_resp_headers   = 8,                        \
43         .backlog_conn       = 5,                        \
44         .lru_purge_enable   = false,                    \
45         .recv_wait_timeout  = 5,                        \
46         .send_wait_timeout  = 5,                        \
47         .global_user_ctx = NULL,                        \
48         .global_user_ctx_free_fn = NULL,                \
49         .global_transport_ctx = NULL,                   \
50         .global_transport_ctx_free_fn = NULL,           \
51         .open_fn = NULL,                                \
52         .close_fn = NULL,                               \
53         .uri_match_fn = NULL                            \
54 }
55 
56 #define ESP_ERR_HTTPD_BASE              (0xb000)                    /*!< Starting number of HTTPD error codes */
57 #define ESP_ERR_HTTPD_HANDLERS_FULL     (ESP_ERR_HTTPD_BASE +  1)   /*!< All slots for registering URI handlers have been consumed */
58 #define ESP_ERR_HTTPD_HANDLER_EXISTS    (ESP_ERR_HTTPD_BASE +  2)   /*!< URI handler with same method and target URI already registered */
59 #define ESP_ERR_HTTPD_INVALID_REQ       (ESP_ERR_HTTPD_BASE +  3)   /*!< Invalid request pointer */
60 #define ESP_ERR_HTTPD_RESULT_TRUNC      (ESP_ERR_HTTPD_BASE +  4)   /*!< Result string truncated */
61 #define ESP_ERR_HTTPD_RESP_HDR          (ESP_ERR_HTTPD_BASE +  5)   /*!< Response header field larger than supported */
62 #define ESP_ERR_HTTPD_RESP_SEND         (ESP_ERR_HTTPD_BASE +  6)   /*!< Error occured while sending response packet */
63 #define ESP_ERR_HTTPD_ALLOC_MEM         (ESP_ERR_HTTPD_BASE +  7)   /*!< Failed to dynamically allocate memory for resource */
64 #define ESP_ERR_HTTPD_TASK              (ESP_ERR_HTTPD_BASE +  8)   /*!< Failed to launch server task/thread */
65 
66 /* Symbol to be used as length parameter in httpd_resp_send APIs
67  * for setting buffer length to string length */
68 #define HTTPD_RESP_USE_STRLEN -1
69 
70 /* ************** Group: Initialization ************** */
71 /** @name Initialization
72  * APIs related to the Initialization of the web server
73  * @{
74  */
75 
76 /**
77  * @brief   HTTP Server Instance Handle
78  *
79  * Every instance of the server will have a unique handle.
80  */
81 typedef void* httpd_handle_t;
82 
83 /**
84  * @brief   HTTP Method Type wrapper over "enum http_method"
85  *          available in "http_parser" library
86  */
87 typedef enum http_method httpd_method_t;
88 
89 /**
90  * @brief  Prototype for freeing context data (if any)
91  * @param[in] ctx   object to free
92  */
93 typedef void (*httpd_free_ctx_fn_t)(void *ctx);
94 
95 /**
96  * @brief  Function prototype for opening a session.
97  *
98  * Called immediately after the socket was opened to set up the send/recv functions and
99  * other parameters of the socket.
100  *
101  * @param[in] hd       server instance
102  * @param[in] sockfd   session socket file descriptor
103  * @return
104  *  - ESP_OK   : On success
105  *  - Any value other than ESP_OK will signal the server to close the socket immediately
106  */
107 typedef esp_err_t (*httpd_open_func_t)(httpd_handle_t hd, int sockfd);
108 
109 /**
110  * @brief  Function prototype for closing a session.
111  *
112  * @note   It's possible that the socket descriptor is invalid at this point, the function
113  *         is called for all terminated sessions. Ensure proper handling of return codes.
114  *
115  * @param[in] hd   server instance
116  * @param[in] sockfd   session socket file descriptor
117  */
118 typedef void (*httpd_close_func_t)(httpd_handle_t hd, int sockfd);
119 
120 /**
121  * @brief  Function prototype for URI matching.
122  *
123  * @param[in] reference_uri   URI/template with respect to which the other URI is matched
124  * @param[in] uri_to_match    URI/template being matched to the reference URI/template
125  * @param[in] match_upto      For specifying the actual length of `uri_to_match` up to
126  *                            which the matching algorithm is to be applied (The maximum
127  *                            value is `strlen(uri_to_match)`, independent of the length
128  *                            of `reference_uri`)
129  * @return true on match
130  */
131 typedef bool (*httpd_uri_match_func_t)(const char *reference_uri,
132                                        const char *uri_to_match,
133                                        size_t match_upto);
134 
135 /**
136  * @brief   HTTP Server Configuration Structure
137  *
138  * @note    Use HTTPD_DEFAULT_CONFIG() to initialize the configuration
139  *          to a default value and then modify only those fields that are
140  *          specifically determined by the use case.
141  */
142 typedef struct httpd_config {
143     unsigned    task_priority;      /*!< Priority of FreeRTOS task which runs the server */
144     size_t      stack_size;         /*!< The maximum stack size allowed for the server task */
145     BaseType_t  core_id;            /*!< The core the HTTP server task will run on */
146 
147     /**
148      * TCP Port number for receiving and transmitting HTTP traffic
149      */
150     uint16_t    server_port;
151 
152     /**
153      * UDP Port number for asynchronously exchanging control signals
154      * between various components of the server
155      */
156     uint16_t    ctrl_port;
157 
158     uint16_t    max_open_sockets;   /*!< Max number of sockets/clients connected at any time*/
159     uint16_t    max_uri_handlers;   /*!< Maximum allowed uri handlers */
160     uint16_t    max_resp_headers;   /*!< Maximum allowed additional headers in HTTP response */
161     uint16_t    backlog_conn;       /*!< Number of backlog connections */
162     bool        lru_purge_enable;   /*!< Purge "Least Recently Used" connection */
163     uint16_t    recv_wait_timeout;  /*!< Timeout for recv function (in seconds)*/
164     uint16_t    send_wait_timeout;  /*!< Timeout for send function (in seconds)*/
165 
166     /**
167      * Global user context.
168      *
169      * This field can be used to store arbitrary user data within the server context.
170      * The value can be retrieved using the server handle, available e.g. in the httpd_req_t struct.
171      *
172      * When shutting down, the server frees up the user context by
173      * calling free() on the global_user_ctx field. If you wish to use a custom
174      * function for freeing the global user context, please specify that here.
175      */
176     void * global_user_ctx;
177 
178     /**
179      * Free function for global user context
180      */
181     httpd_free_ctx_fn_t global_user_ctx_free_fn;
182 
183     /**
184      * Global transport context.
185      *
186      * Similar to global_user_ctx, but used for session encoding or encryption (e.g. to hold the SSL context).
187      * It will be freed using free(), unless global_transport_ctx_free_fn is specified.
188      */
189     void * global_transport_ctx;
190 
191     /**
192      * Free function for global transport context
193      */
194     httpd_free_ctx_fn_t global_transport_ctx_free_fn;
195 
196     /**
197      * Custom session opening callback.
198      *
199      * Called on a new session socket just after accept(), but before reading any data.
200      *
201      * This is an opportunity to set up e.g. SSL encryption using global_transport_ctx
202      * and the send/recv/pending session overrides.
203      *
204      * If a context needs to be maintained between these functions, store it in the session using
205      * httpd_sess_set_transport_ctx() and retrieve it later with httpd_sess_get_transport_ctx()
206      *
207      * Returning a value other than ESP_OK will immediately close the new socket.
208      */
209     httpd_open_func_t open_fn;
210 
211     /**
212      * Custom session closing callback.
213      *
214      * Called when a session is deleted, before freeing user and transport contexts and before
215      * closing the socket. This is a place for custom de-init code common to all sockets.
216      *
217      * Set the user or transport context to NULL if it was freed here, so the server does not
218      * try to free it again.
219      *
220      * This function is run for all terminated sessions, including sessions where the socket
221      * was closed by the network stack - that is, the file descriptor may not be valid anymore.
222      */
223     httpd_close_func_t close_fn;
224 
225     /**
226      * URI matcher function.
227      *
228      * Called when searching for a matching URI:
229      *     1) whose request handler is to be executed right
230      *        after an HTTP request is successfully parsed
231      *     2) in order to prevent duplication while registering
232      *        a new URI handler using `httpd_register_uri_handler()`
233      *
234      * Available options are:
235      *     1) NULL : Internally do basic matching using `strncmp()`
236      *     2) `httpd_uri_match_wildcard()` : URI wildcard matcher
237      *
238      * Users can implement their own matching functions (See description
239      * of the `httpd_uri_match_func_t` function prototype)
240      */
241     httpd_uri_match_func_t uri_match_fn;
242 } httpd_config_t;
243 
244 /**
245  * @brief Starts the web server
246  *
247  * Create an instance of HTTP server and allocate memory/resources for it
248  * depending upon the specified configuration.
249  *
250  * Example usage:
251  * @code{c}
252  *
253  * //Function for starting the webserver
254  * httpd_handle_t start_webserver(void)
255  * {
256  *      // Generate default configuration
257  *      httpd_config_t config = HTTPD_DEFAULT_CONFIG();
258  *
259  *      // Empty handle to http_server
260  *      httpd_handle_t server = NULL;
261  *
262  *      // Start the httpd server
263  *      if (httpd_start(&server, &config) == ESP_OK) {
264  *          // Register URI handlers
265  *          httpd_register_uri_handler(server, &uri_get);
266  *          httpd_register_uri_handler(server, &uri_post);
267  *      }
268  *      // If server failed to start, handle will be NULL
269  *      return server;
270  * }
271  *
272  * @endcode
273  *
274  * @param[in]  config   Configuration for new instance of the server
275  * @param[out] handle   Handle to newly created instance of the server. NULL on error
276  * @return
277  *  - ESP_OK    : Instance created successfully
278  *  - ESP_ERR_INVALID_ARG      : Null argument(s)
279  *  - ESP_ERR_HTTPD_ALLOC_MEM  : Failed to allocate memory for instance
280  *  - ESP_ERR_HTTPD_TASK       : Failed to launch server task
281  */
282 esp_err_t  httpd_start(httpd_handle_t *handle, const httpd_config_t *config);
283 
284 /**
285  * @brief Stops the web server
286  *
287  * Deallocates memory/resources used by an HTTP server instance and
288  * deletes it. Once deleted the handle can no longer be used for accessing
289  * the instance.
290  *
291  * Example usage:
292  * @code{c}
293  *
294  * // Function for stopping the webserver
295  * void stop_webserver(httpd_handle_t server)
296  * {
297  *      // Ensure handle is non NULL
298  *      if (server != NULL) {
299  *          // Stop the httpd server
300  *          httpd_stop(server);
301  *      }
302  * }
303  *
304  * @endcode
305  *
306  * @param[in] handle Handle to server returned by httpd_start
307  * @return
308  *  - ESP_OK : Server stopped successfully
309  *  - ESP_ERR_INVALID_ARG : Handle argument is Null
310  */
311 esp_err_t httpd_stop(httpd_handle_t handle);
312 
313 /** End of Group Initialization
314  * @}
315  */
316 
317 /* ************** Group: URI Handlers ************** */
318 /** @name URI Handlers
319  * APIs related to the URI handlers
320  * @{
321  */
322 
323 /* Max supported HTTP request header length */
324 #define HTTPD_MAX_REQ_HDR_LEN CONFIG_HTTPD_MAX_REQ_HDR_LEN
325 
326 /* Max supported HTTP request URI length */
327 #define HTTPD_MAX_URI_LEN CONFIG_HTTPD_MAX_URI_LEN
328 
329 /**
330  * @brief HTTP Request Data Structure
331  */
332 typedef struct httpd_req {
333     httpd_handle_t  handle;                     /*!< Handle to server instance */
334     int             method;                     /*!< The type of HTTP request, -1 if unsupported method */
335     const char      uri[HTTPD_MAX_URI_LEN + 1]; /*!< The URI of this request (1 byte extra for null termination) */
336     size_t          content_len;                /*!< Length of the request body */
337     void           *aux;                        /*!< Internally used members */
338 
339     /**
340      * User context pointer passed during URI registration.
341      */
342     void *user_ctx;
343 
344     /**
345      * Session Context Pointer
346      *
347      * A session context. Contexts are maintained across 'sessions' for a
348      * given open TCP connection. One session could have multiple request
349      * responses. The web server will ensure that the context persists
350      * across all these request and responses.
351      *
352      * By default, this is NULL. URI Handlers can set this to any meaningful
353      * value.
354      *
355      * If the underlying socket gets closed, and this pointer is non-NULL,
356      * the web server will free up the context by calling free(), unless
357      * free_ctx function is set.
358      */
359     void *sess_ctx;
360 
361     /**
362      * Pointer to free context hook
363      *
364      * Function to free session context
365      *
366      * If the web server's socket closes, it frees up the session context by
367      * calling free() on the sess_ctx member. If you wish to use a custom
368      * function for freeing the session context, please specify that here.
369      */
370     httpd_free_ctx_fn_t free_ctx;
371 
372     /**
373      * Flag indicating if Session Context changes should be ignored
374      *
375      * By default, if you change the sess_ctx in some URI handler, the http server
376      * will internally free the earlier context (if non NULL), after the URI handler
377      * returns. If you want to manage the allocation/reallocation/freeing of
378      * sess_ctx yourself, set this flag to true, so that the server will not
379      * perform any checks on it. The context will be cleared by the server
380      * (by calling free_ctx or free()) only if the socket gets closed.
381      */
382     bool ignore_sess_ctx_changes;
383 } httpd_req_t;
384 
385 /**
386  * @brief Structure for URI handler
387  */
388 typedef struct httpd_uri {
389     const char       *uri;    /*!< The URI to handle */
390     httpd_method_t    method; /*!< Method supported by the URI */
391 
392     /**
393      * Handler to call for supported request method. This must
394      * return ESP_OK, or else the underlying socket will be closed.
395      */
396     esp_err_t (*handler)(httpd_req_t *r);
397 
398     /**
399      * Pointer to user context data which will be available to handler
400      */
401     void *user_ctx;
402 
403 #ifdef CONFIG_HTTPD_WS_SUPPORT
404     /**
405      * Flag for indicating a WebSocket endpoint.
406      * If this flag is true, then method must be HTTP_GET. Otherwise the handshake will not be handled.
407      */
408     bool is_websocket;
409 
410     /**
411      * Flag indicating that control frames (PING, PONG, CLOSE) are also passed to the handler
412      * This is used if a custom processing of the control frames is needed
413      */
414     bool handle_ws_control_frames;
415 
416     /**
417      * Pointer to subprotocol supported by URI
418      */
419     const char *supported_subprotocol;
420 #endif
421 } httpd_uri_t;
422 
423 /**
424  * @brief   Registers a URI handler
425  *
426  * @note    URI handlers can be registered in real time as long as the
427  *          server handle is valid.
428  *
429  * Example usage:
430  * @code{c}
431  *
432  * esp_err_t my_uri_handler(httpd_req_t* req)
433  * {
434  *     // Recv , Process and Send
435  *     ....
436  *     ....
437  *     ....
438  *
439  *     // Fail condition
440  *     if (....) {
441  *         // Return fail to close session //
442  *         return ESP_FAIL;
443  *     }
444  *
445  *     // On success
446  *     return ESP_OK;
447  * }
448  *
449  * // URI handler structure
450  * httpd_uri_t my_uri {
451  *     .uri      = "/my_uri/path/xyz",
452  *     .method   = HTTPD_GET,
453  *     .handler  = my_uri_handler,
454  *     .user_ctx = NULL
455  * };
456  *
457  * // Register handler
458  * if (httpd_register_uri_handler(server_handle, &my_uri) != ESP_OK) {
459  *    // If failed to register handler
460  *    ....
461  * }
462  *
463  * @endcode
464  *
465  * @param[in] handle      handle to HTTPD server instance
466  * @param[in] uri_handler pointer to handler that needs to be registered
467  *
468  * @return
469  *  - ESP_OK : On successfully registering the handler
470  *  - ESP_ERR_INVALID_ARG : Null arguments
471  *  - ESP_ERR_HTTPD_HANDLERS_FULL  : If no slots left for new handler
472  *  - ESP_ERR_HTTPD_HANDLER_EXISTS : If handler with same URI and
473  *                                   method is already registered
474  */
475 esp_err_t httpd_register_uri_handler(httpd_handle_t handle,
476                                      const httpd_uri_t *uri_handler);
477 
478 /**
479  * @brief   Unregister a URI handler
480  *
481  * @param[in] handle    handle to HTTPD server instance
482  * @param[in] uri       URI string
483  * @param[in] method    HTTP method
484  *
485  * @return
486  *  - ESP_OK : On successfully deregistering the handler
487  *  - ESP_ERR_INVALID_ARG : Null arguments
488  *  - ESP_ERR_NOT_FOUND   : Handler with specified URI and method not found
489  */
490 esp_err_t httpd_unregister_uri_handler(httpd_handle_t handle,
491                                        const char *uri, httpd_method_t method);
492 
493 /**
494  * @brief   Unregister all URI handlers with the specified uri string
495  *
496  * @param[in] handle   handle to HTTPD server instance
497  * @param[in] uri      uri string specifying all handlers that need
498  *                     to be deregisterd
499  *
500  * @return
501  *  - ESP_OK : On successfully deregistering all such handlers
502  *  - ESP_ERR_INVALID_ARG : Null arguments
503  *  - ESP_ERR_NOT_FOUND   : No handler registered with specified uri string
504  */
505 esp_err_t httpd_unregister_uri(httpd_handle_t handle, const char* uri);
506 
507 /** End of URI Handlers
508  * @}
509  */
510 
511 /* ************** Group: HTTP Error ************** */
512 /** @name HTTP Error
513  * Prototype for HTTP errors and error handling functions
514  * @{
515  */
516 
517 /**
518  * @brief Error codes sent as HTTP response in case of errors
519  *        encountered during processing of an HTTP request
520  */
521 typedef enum {
522     /* For any unexpected errors during parsing, like unexpected
523      * state transitions, or unhandled errors.
524      */
525     HTTPD_500_INTERNAL_SERVER_ERROR = 0,
526 
527     /* For methods not supported by http_parser. Presently
528      * http_parser halts parsing when such methods are
529      * encountered and so the server responds with 400 Bad
530      * Request error instead.
531      */
532     HTTPD_501_METHOD_NOT_IMPLEMENTED,
533 
534     /* When HTTP version is not 1.1 */
535     HTTPD_505_VERSION_NOT_SUPPORTED,
536 
537     /* Returned when http_parser halts parsing due to incorrect
538      * syntax of request, unsupported method in request URI or
539      * due to chunked encoding / upgrade field present in headers
540      */
541     HTTPD_400_BAD_REQUEST,
542 
543     /* This response means the client must authenticate itself
544      * to get the requested response.
545      */
546     HTTPD_401_UNAUTHORIZED,
547 
548     /* The client does not have access rights to the content,
549      * so the server is refusing to give the requested resource.
550      * Unlike 401, the client's identity is known to the server.
551      */
552     HTTPD_403_FORBIDDEN,
553 
554     /* When requested URI is not found */
555     HTTPD_404_NOT_FOUND,
556 
557     /* When URI found, but method has no handler registered */
558     HTTPD_405_METHOD_NOT_ALLOWED,
559 
560     /* Intended for recv timeout. Presently it's being sent
561      * for other recv errors as well. Client should expect the
562      * server to immediately close the connection after
563      * responding with this.
564      */
565     HTTPD_408_REQ_TIMEOUT,
566 
567     /* Intended for responding to chunked encoding, which is
568      * not supported currently. Though unhandled http_parser
569      * callback for chunked request returns "400 Bad Request"
570      */
571     HTTPD_411_LENGTH_REQUIRED,
572 
573     /* URI length greater than CONFIG_HTTPD_MAX_URI_LEN */
574     HTTPD_414_URI_TOO_LONG,
575 
576     /* Headers section larger than CONFIG_HTTPD_MAX_REQ_HDR_LEN */
577     HTTPD_431_REQ_HDR_FIELDS_TOO_LARGE,
578 
579     /* Used internally for retrieving the total count of errors */
580     HTTPD_ERR_CODE_MAX
581 } httpd_err_code_t;
582 
583 /**
584  * @brief  Function prototype for HTTP error handling.
585  *
586  * This function is executed upon HTTP errors generated during
587  * internal processing of an HTTP request. This is used to override
588  * the default behavior on error, which is to send HTTP error response
589  * and close the underlying socket.
590  *
591  * @note
592  *  - If implemented, the server will not automatically send out HTTP
593  *    error response codes, therefore, httpd_resp_send_err() must be
594  *    invoked inside this function if user wishes to generate HTTP
595  *    error responses.
596  *  - When invoked, the validity of `uri`, `method`, `content_len`
597  *    and `user_ctx` fields of the httpd_req_t parameter is not
598  *    guaranteed as the HTTP request may be partially received/parsed.
599  *  - The function must return ESP_OK if underlying socket needs to
600  *    be kept open. Any other value will ensure that the socket is
601  *    closed. The return value is ignored when error is of type
602  *    `HTTPD_500_INTERNAL_SERVER_ERROR` and the socket closed anyway.
603  *
604  * @param[in] req    HTTP request for which the error needs to be handled
605  * @param[in] error  Error type
606  *
607  * @return
608  *  - ESP_OK   : error handled successful
609  *  - ESP_FAIL : failure indicates that the underlying socket needs to be closed
610  */
611 typedef esp_err_t (*httpd_err_handler_func_t)(httpd_req_t *req,
612                                               httpd_err_code_t error);
613 
614 /**
615  * @brief  Function for registering HTTP error handlers
616  *
617  * This function maps a handler function to any supported error code
618  * given by `httpd_err_code_t`. See prototype `httpd_err_handler_func_t`
619  * above for details.
620  *
621  * @param[in] handle     HTTP server handle
622  * @param[in] error      Error type
623  * @param[in] handler_fn User implemented handler function
624  *                       (Pass NULL to unset any previously set handler)
625  *
626  * @return
627  *  - ESP_OK : handler registered successfully
628  *  - ESP_ERR_INVALID_ARG : invalid error code or server handle
629  */
630 esp_err_t httpd_register_err_handler(httpd_handle_t handle,
631                                      httpd_err_code_t error,
632                                      httpd_err_handler_func_t handler_fn);
633 
634 /** End of HTTP Error
635  * @}
636  */
637 
638 /* ************** Group: TX/RX ************** */
639 /** @name TX / RX
640  * Prototype for HTTPDs low-level send/recv functions
641  * @{
642  */
643 
644 #define HTTPD_SOCK_ERR_FAIL      -1
645 #define HTTPD_SOCK_ERR_INVALID   -2
646 #define HTTPD_SOCK_ERR_TIMEOUT   -3
647 
648 /**
649  * @brief  Prototype for HTTPDs low-level send function
650  *
651  * @note   User specified send function must handle errors internally,
652  *         depending upon the set value of errno, and return specific
653  *         HTTPD_SOCK_ERR_ codes, which will eventually be conveyed as
654  *         return value of httpd_send() function
655  *
656  * @param[in] hd        server instance
657  * @param[in] sockfd    session socket file descriptor
658  * @param[in] buf       buffer with bytes to send
659  * @param[in] buf_len   data size
660  * @param[in] flags     flags for the send() function
661  * @return
662  *  - Bytes : The number of bytes sent successfully
663  *  - HTTPD_SOCK_ERR_INVALID  : Invalid arguments
664  *  - HTTPD_SOCK_ERR_TIMEOUT  : Timeout/interrupted while calling socket send()
665  *  - HTTPD_SOCK_ERR_FAIL     : Unrecoverable error while calling socket send()
666  */
667 typedef int (*httpd_send_func_t)(httpd_handle_t hd, int sockfd, const char *buf, size_t buf_len, int flags);
668 
669 /**
670  * @brief  Prototype for HTTPDs low-level recv function
671  *
672  * @note   User specified recv function must handle errors internally,
673  *         depending upon the set value of errno, and return specific
674  *         HTTPD_SOCK_ERR_ codes, which will eventually be conveyed as
675  *         return value of httpd_req_recv() function
676  *
677  * @param[in] hd        server instance
678  * @param[in] sockfd    session socket file descriptor
679  * @param[in] buf       buffer with bytes to send
680  * @param[in] buf_len   data size
681  * @param[in] flags     flags for the send() function
682  * @return
683  *  - Bytes : The number of bytes received successfully
684  *  - 0     : Buffer length parameter is zero / connection closed by peer
685  *  - HTTPD_SOCK_ERR_INVALID  : Invalid arguments
686  *  - HTTPD_SOCK_ERR_TIMEOUT  : Timeout/interrupted while calling socket recv()
687  *  - HTTPD_SOCK_ERR_FAIL     : Unrecoverable error while calling socket recv()
688  */
689 typedef int (*httpd_recv_func_t)(httpd_handle_t hd, int sockfd, char *buf, size_t buf_len, int flags);
690 
691 /**
692  * @brief  Prototype for HTTPDs low-level "get pending bytes" function
693  *
694  * @note   User specified pending function must handle errors internally,
695  *         depending upon the set value of errno, and return specific
696  *         HTTPD_SOCK_ERR_ codes, which will be handled accordingly in
697  *         the server task.
698  *
699  * @param[in] hd       server instance
700  * @param[in] sockfd   session socket file descriptor
701  * @return
702  *  - Bytes : The number of bytes waiting to be received
703  *  - HTTPD_SOCK_ERR_INVALID  : Invalid arguments
704  *  - HTTPD_SOCK_ERR_TIMEOUT  : Timeout/interrupted while calling socket pending()
705  *  - HTTPD_SOCK_ERR_FAIL     : Unrecoverable error while calling socket pending()
706  */
707 typedef int (*httpd_pending_func_t)(httpd_handle_t hd, int sockfd);
708 
709 /** End of TX / RX
710  * @}
711  */
712 
713 /* ************** Group: Request/Response ************** */
714 /** @name Request / Response
715  * APIs related to the data send/receive by URI handlers.
716  * These APIs are supposed to be called only from the context of
717  * a URI handler where httpd_req_t* request pointer is valid.
718  * @{
719  */
720 
721 /**
722  * @brief   Override web server's receive function (by session FD)
723  *
724  * This function overrides the web server's receive function. This same function is
725  * used to read HTTP request packets.
726  *
727  * @note    This API is supposed to be called either from the context of
728  *          - an http session APIs where sockfd is a valid parameter
729  *          - a URI handler where sockfd is obtained using httpd_req_to_sockfd()
730  *
731  * @param[in] hd        HTTPD instance handle
732  * @param[in] sockfd    Session socket FD
733  * @param[in] recv_func The receive function to be set for this session
734  *
735  * @return
736  *  - ESP_OK : On successfully registering override
737  *  - ESP_ERR_INVALID_ARG : Null arguments
738  */
739 esp_err_t httpd_sess_set_recv_override(httpd_handle_t hd, int sockfd, httpd_recv_func_t recv_func);
740 
741 /**
742  * @brief   Override web server's send function (by session FD)
743  *
744  * This function overrides the web server's send function. This same function is
745  * used to send out any response to any HTTP request.
746  *
747  * @note    This API is supposed to be called either from the context of
748  *          - an http session APIs where sockfd is a valid parameter
749  *          - a URI handler where sockfd is obtained using httpd_req_to_sockfd()
750  *
751  * @param[in] hd        HTTPD instance handle
752  * @param[in] sockfd    Session socket FD
753  * @param[in] send_func The send function to be set for this session
754  *
755  * @return
756  *  - ESP_OK : On successfully registering override
757  *  - ESP_ERR_INVALID_ARG : Null arguments
758  */
759 esp_err_t httpd_sess_set_send_override(httpd_handle_t hd, int sockfd, httpd_send_func_t send_func);
760 
761 /**
762  * @brief   Override web server's pending function (by session FD)
763  *
764  * This function overrides the web server's pending function. This function is
765  * used to test for pending bytes in a socket.
766  *
767  * @note    This API is supposed to be called either from the context of
768  *          - an http session APIs where sockfd is a valid parameter
769  *          - a URI handler where sockfd is obtained using httpd_req_to_sockfd()
770  *
771  * @param[in] hd           HTTPD instance handle
772  * @param[in] sockfd       Session socket FD
773  * @param[in] pending_func The receive function to be set for this session
774  *
775  * @return
776  *  - ESP_OK : On successfully registering override
777  *  - ESP_ERR_INVALID_ARG : Null arguments
778  */
779 esp_err_t httpd_sess_set_pending_override(httpd_handle_t hd, int sockfd, httpd_pending_func_t pending_func);
780 
781 /**
782  * @brief   Get the Socket Descriptor from the HTTP request
783  *
784  * This API will return the socket descriptor of the session for
785  * which URI handler was executed on reception of HTTP request.
786  * This is useful when user wants to call functions that require
787  * session socket fd, from within a URI handler, ie. :
788  *      httpd_sess_get_ctx(),
789  *      httpd_sess_trigger_close(),
790  *      httpd_sess_update_lru_counter().
791  *
792  * @note    This API is supposed to be called only from the context of
793  *          a URI handler where httpd_req_t* request pointer is valid.
794  *
795  * @param[in] r The request whose socket descriptor should be found
796  *
797  * @return
798  *  - Socket descriptor : The socket descriptor for this request
799  *  - -1 : Invalid/NULL request pointer
800  */
801 int httpd_req_to_sockfd(httpd_req_t *r);
802 
803 /**
804  * @brief   API to read content data from the HTTP request
805  *
806  * This API will read HTTP content data from the HTTP request into
807  * provided buffer. Use content_len provided in httpd_req_t structure
808  * to know the length of data to be fetched. If content_len is too
809  * large for the buffer then user may have to make multiple calls to
810  * this function, each time fetching 'buf_len' number of bytes,
811  * while the pointer to content data is incremented internally by
812  * the same number.
813  *
814  * @note
815  *  - This API is supposed to be called only from the context of
816  *    a URI handler where httpd_req_t* request pointer is valid.
817  *  - If an error is returned, the URI handler must further return an error.
818  *    This will ensure that the erroneous socket is closed and cleaned up by
819  *    the web server.
820  *  - Presently Chunked Encoding is not supported
821  *
822  * @param[in] r         The request being responded to
823  * @param[in] buf       Pointer to a buffer that the data will be read into
824  * @param[in] buf_len   Length of the buffer
825  *
826  * @return
827  *  - Bytes : Number of bytes read into the buffer successfully
828  *  - 0     : Buffer length parameter is zero / connection closed by peer
829  *  - HTTPD_SOCK_ERR_INVALID  : Invalid arguments
830  *  - HTTPD_SOCK_ERR_TIMEOUT  : Timeout/interrupted while calling socket recv()
831  *  - HTTPD_SOCK_ERR_FAIL     : Unrecoverable error while calling socket recv()
832  */
833 int httpd_req_recv(httpd_req_t *r, char *buf, size_t buf_len);
834 
835 /**
836  * @brief   Search for a field in request headers and
837  *          return the string length of it's value
838  *
839  * @note
840  *  - This API is supposed to be called only from the context of
841  *    a URI handler where httpd_req_t* request pointer is valid.
842  *  - Once httpd_resp_send() API is called all request headers
843  *    are purged, so request headers need be copied into separate
844  *    buffers if they are required later.
845  *
846  * @param[in]  r        The request being responded to
847  * @param[in]  field    The header field to be searched in the request
848  *
849  * @return
850  *  - Length    : If field is found in the request URL
851  *  - Zero      : Field not found / Invalid request / Null arguments
852  */
853 size_t httpd_req_get_hdr_value_len(httpd_req_t *r, const char *field);
854 
855 /**
856  * @brief   Get the value string of a field from the request headers
857  *
858  * @note
859  *  - This API is supposed to be called only from the context of
860  *    a URI handler where httpd_req_t* request pointer is valid.
861  *  - Once httpd_resp_send() API is called all request headers
862  *    are purged, so request headers need be copied into separate
863  *    buffers if they are required later.
864  *  - If output size is greater than input, then the value is truncated,
865  *    accompanied by truncation error as return value.
866  *  - Use httpd_req_get_hdr_value_len() to know the right buffer length
867  *
868  * @param[in]  r        The request being responded to
869  * @param[in]  field    The field to be searched in the header
870  * @param[out] val      Pointer to the buffer into which the value will be copied if the field is found
871  * @param[in]  val_size Size of the user buffer "val"
872  *
873  * @return
874  *  - ESP_OK : Field found in the request header and value string copied
875  *  - ESP_ERR_NOT_FOUND          : Key not found
876  *  - ESP_ERR_INVALID_ARG        : Null arguments
877  *  - ESP_ERR_HTTPD_INVALID_REQ  : Invalid HTTP request pointer
878  *  - ESP_ERR_HTTPD_RESULT_TRUNC : Value string truncated
879  */
880 esp_err_t httpd_req_get_hdr_value_str(httpd_req_t *r, const char *field, char *val, size_t val_size);
881 
882 /**
883  * @brief   Get Query string length from the request URL
884  *
885  * @note    This API is supposed to be called only from the context of
886  *          a URI handler where httpd_req_t* request pointer is valid
887  *
888  * @param[in]  r    The request being responded to
889  *
890  * @return
891  *  - Length    : Query is found in the request URL
892  *  - Zero      : Query not found / Null arguments / Invalid request
893  */
894 size_t httpd_req_get_url_query_len(httpd_req_t *r);
895 
896 /**
897  * @brief   Get Query string from the request URL
898  *
899  * @note
900  *  - Presently, the user can fetch the full URL query string, but decoding
901  *    will have to be performed by the user. Request headers can be read using
902  *    httpd_req_get_hdr_value_str() to know the 'Content-Type' (eg. Content-Type:
903  *    application/x-www-form-urlencoded) and then the appropriate decoding
904  *    algorithm needs to be applied.
905  *  - This API is supposed to be called only from the context of
906  *    a URI handler where httpd_req_t* request pointer is valid
907  *  - If output size is greater than input, then the value is truncated,
908  *    accompanied by truncation error as return value
909  *  - Prior to calling this function, one can use httpd_req_get_url_query_len()
910  *    to know the query string length beforehand and hence allocate the buffer
911  *    of right size (usually query string length + 1 for null termination)
912  *    for storing the query string
913  *
914  * @param[in]  r         The request being responded to
915  * @param[out] buf       Pointer to the buffer into which the query string will be copied (if found)
916  * @param[in]  buf_len   Length of output buffer
917  *
918  * @return
919  *  - ESP_OK : Query is found in the request URL and copied to buffer
920  *  - ESP_ERR_NOT_FOUND          : Query not found
921  *  - ESP_ERR_INVALID_ARG        : Null arguments
922  *  - ESP_ERR_HTTPD_INVALID_REQ  : Invalid HTTP request pointer
923  *  - ESP_ERR_HTTPD_RESULT_TRUNC : Query string truncated
924  */
925 esp_err_t httpd_req_get_url_query_str(httpd_req_t *r, char *buf, size_t buf_len);
926 
927 /**
928  * @brief   Helper function to get a URL query tag from a query
929  *          string of the type param1=val1&param2=val2
930  *
931  * @note
932  *  - The components of URL query string (keys and values) are not URLdecoded.
933  *    The user must check for 'Content-Type' field in the request headers and
934  *    then depending upon the specified encoding (URLencoded or otherwise) apply
935  *    the appropriate decoding algorithm.
936  *  - If actual value size is greater than val_size, then the value is truncated,
937  *    accompanied by truncation error as return value.
938  *
939  * @param[in]  qry       Pointer to query string
940  * @param[in]  key       The key to be searched in the query string
941  * @param[out] val       Pointer to the buffer into which the value will be copied if the key is found
942  * @param[in]  val_size  Size of the user buffer "val"
943  *
944  * @return
945  *  - ESP_OK : Key is found in the URL query string and copied to buffer
946  *  - ESP_ERR_NOT_FOUND          : Key not found
947  *  - ESP_ERR_INVALID_ARG        : Null arguments
948  *  - ESP_ERR_HTTPD_RESULT_TRUNC : Value string truncated
949  */
950 esp_err_t httpd_query_key_value(const char *qry, const char *key, char *val, size_t val_size);
951 
952 /**
953  * @brief Test if a URI matches the given wildcard template.
954  *
955  * Template may end with "?" to make the previous character optional (typically a slash),
956  * "*" for a wildcard match, and "?*" to make the previous character optional, and if present,
957  * allow anything to follow.
958  *
959  * Example:
960  *   - * matches everything
961  *   - /foo/? matches /foo and /foo/
962  *   - /foo/\* (sans the backslash) matches /foo/ and /foo/bar, but not /foo or /fo
963  *   - /foo/?* or /foo/\*?  (sans the backslash) matches /foo/, /foo/bar, and also /foo, but not /foox or /fo
964  *
965  * The special characters "?" and "*" anywhere else in the template will be taken literally.
966  *
967  * @param[in] uri_template   URI template (pattern)
968  * @param[in] uri_to_match   URI to be matched
969  * @param[in] match_upto     how many characters of the URI buffer to test
970  *                          (there may be trailing query string etc.)
971  *
972  * @return true if a match was found
973  */
974 bool httpd_uri_match_wildcard(const char *uri_template, const char *uri_to_match, size_t match_upto);
975 
976 /**
977  * @brief   API to send a complete HTTP response.
978  *
979  * This API will send the data as an HTTP response to the request.
980  * This assumes that you have the entire response ready in a single
981  * buffer. If you wish to send response in incremental chunks use
982  * httpd_resp_send_chunk() instead.
983  *
984  * If no status code and content-type were set, by default this
985  * will send 200 OK status code and content type as text/html.
986  * You may call the following functions before this API to configure
987  * the response headers :
988  *      httpd_resp_set_status() - for setting the HTTP status string,
989  *      httpd_resp_set_type()   - for setting the Content Type,
990  *      httpd_resp_set_hdr()    - for appending any additional field
991  *                                value entries in the response header
992  *
993  * @note
994  *  - This API is supposed to be called only from the context of
995  *    a URI handler where httpd_req_t* request pointer is valid.
996  *  - Once this API is called, the request has been responded to.
997  *  - No additional data can then be sent for the request.
998  *  - Once this API is called, all request headers are purged, so
999  *    request headers need be copied into separate buffers if
1000  *    they are required later.
1001  *
1002  * @param[in] r         The request being responded to
1003  * @param[in] buf       Buffer from where the content is to be fetched
1004  * @param[in] buf_len   Length of the buffer, HTTPD_RESP_USE_STRLEN to use strlen()
1005  *
1006  * @return
1007  *  - ESP_OK : On successfully sending the response packet
1008  *  - ESP_ERR_INVALID_ARG : Null request pointer
1009  *  - ESP_ERR_HTTPD_RESP_HDR    : Essential headers are too large for internal buffer
1010  *  - ESP_ERR_HTTPD_RESP_SEND   : Error in raw send
1011  *  - ESP_ERR_HTTPD_INVALID_REQ : Invalid request
1012  */
1013 esp_err_t httpd_resp_send(httpd_req_t *r, const char *buf, ssize_t buf_len);
1014 
1015 /**
1016  * @brief   API to send one HTTP chunk
1017  *
1018  * This API will send the data as an HTTP response to the
1019  * request. This API will use chunked-encoding and send the response
1020  * in the form of chunks. If you have the entire response contained in
1021  * a single buffer, please use httpd_resp_send() instead.
1022  *
1023  * If no status code and content-type were set, by default this will
1024  * send 200 OK status code and content type as text/html. You may
1025  * call the following functions before this API to configure the
1026  * response headers
1027  *      httpd_resp_set_status() - for setting the HTTP status string,
1028  *      httpd_resp_set_type()   - for setting the Content Type,
1029  *      httpd_resp_set_hdr()    - for appending any additional field
1030  *                                value entries in the response header
1031  *
1032  * @note
1033  * - This API is supposed to be called only from the context of
1034  *   a URI handler where httpd_req_t* request pointer is valid.
1035  * - When you are finished sending all your chunks, you must call
1036  *   this function with buf_len as 0.
1037  * - Once this API is called, all request headers are purged, so
1038  *   request headers need be copied into separate buffers if they
1039  *   are required later.
1040  *
1041  * @param[in] r         The request being responded to
1042  * @param[in] buf       Pointer to a buffer that stores the data
1043  * @param[in] buf_len   Length of the buffer, HTTPD_RESP_USE_STRLEN to use strlen()
1044  *
1045  * @return
1046  *  - ESP_OK : On successfully sending the response packet chunk
1047  *  - ESP_ERR_INVALID_ARG : Null request pointer
1048  *  - ESP_ERR_HTTPD_RESP_HDR    : Essential headers are too large for internal buffer
1049  *  - ESP_ERR_HTTPD_RESP_SEND   : Error in raw send
1050  *  - ESP_ERR_HTTPD_INVALID_REQ : Invalid request pointer
1051  */
1052 esp_err_t httpd_resp_send_chunk(httpd_req_t *r, const char *buf, ssize_t buf_len);
1053 
1054 /**
1055  * @brief   API to send a complete string as HTTP response.
1056  *
1057  * This API simply calls http_resp_send with buffer length
1058  * set to string length assuming the buffer contains a null
1059  * terminated string
1060  *
1061  * @param[in] r         The request being responded to
1062  * @param[in] str       String to be sent as response body
1063  *
1064  * @return
1065  *  - ESP_OK : On successfully sending the response packet
1066  *  - ESP_ERR_INVALID_ARG : Null request pointer
1067  *  - ESP_ERR_HTTPD_RESP_HDR    : Essential headers are too large for internal buffer
1068  *  - ESP_ERR_HTTPD_RESP_SEND   : Error in raw send
1069  *  - ESP_ERR_HTTPD_INVALID_REQ : Invalid request
1070  */
httpd_resp_sendstr(httpd_req_t * r,const char * str)1071 static inline esp_err_t httpd_resp_sendstr(httpd_req_t *r, const char *str) {
1072     return httpd_resp_send(r, str, (str == NULL) ? 0 : HTTPD_RESP_USE_STRLEN);
1073 }
1074 
1075 /**
1076  * @brief   API to send a string as an HTTP response chunk.
1077  *
1078  * This API simply calls http_resp_send_chunk with buffer length
1079  * set to string length assuming the buffer contains a null
1080  * terminated string
1081  *
1082  * @param[in] r    The request being responded to
1083  * @param[in] str  String to be sent as response body (NULL to finish response packet)
1084  *
1085  * @return
1086  *  - ESP_OK : On successfully sending the response packet
1087  *  - ESP_ERR_INVALID_ARG : Null request pointer
1088  *  - ESP_ERR_HTTPD_RESP_HDR    : Essential headers are too large for internal buffer
1089  *  - ESP_ERR_HTTPD_RESP_SEND   : Error in raw send
1090  *  - ESP_ERR_HTTPD_INVALID_REQ : Invalid request
1091  */
httpd_resp_sendstr_chunk(httpd_req_t * r,const char * str)1092 static inline esp_err_t httpd_resp_sendstr_chunk(httpd_req_t *r, const char *str) {
1093     return httpd_resp_send_chunk(r, str, (str == NULL) ? 0 : HTTPD_RESP_USE_STRLEN);
1094 }
1095 
1096 /* Some commonly used status codes */
1097 #define HTTPD_200      "200 OK"                     /*!< HTTP Response 200 */
1098 #define HTTPD_204      "204 No Content"             /*!< HTTP Response 204 */
1099 #define HTTPD_207      "207 Multi-Status"           /*!< HTTP Response 207 */
1100 #define HTTPD_400      "400 Bad Request"            /*!< HTTP Response 400 */
1101 #define HTTPD_404      "404 Not Found"              /*!< HTTP Response 404 */
1102 #define HTTPD_408      "408 Request Timeout"        /*!< HTTP Response 408 */
1103 #define HTTPD_500      "500 Internal Server Error"  /*!< HTTP Response 500 */
1104 
1105 /**
1106  * @brief   API to set the HTTP status code
1107  *
1108  * This API sets the status of the HTTP response to the value specified.
1109  * By default, the '200 OK' response is sent as the response.
1110  *
1111  * @note
1112  *  - This API is supposed to be called only from the context of
1113  *    a URI handler where httpd_req_t* request pointer is valid.
1114  *  - This API only sets the status to this value. The status isn't
1115  *    sent out until any of the send APIs is executed.
1116  *  - Make sure that the lifetime of the status string is valid till
1117  *    send function is called.
1118  *
1119  * @param[in] r         The request being responded to
1120  * @param[in] status    The HTTP status code of this response
1121  *
1122  * @return
1123  *  - ESP_OK : On success
1124  *  - ESP_ERR_INVALID_ARG : Null arguments
1125  *  - ESP_ERR_HTTPD_INVALID_REQ : Invalid request pointer
1126  */
1127 esp_err_t httpd_resp_set_status(httpd_req_t *r, const char *status);
1128 
1129 /* Some commonly used content types */
1130 #define HTTPD_TYPE_JSON   "application/json"            /*!< HTTP Content type JSON */
1131 #define HTTPD_TYPE_TEXT   "text/html"                   /*!< HTTP Content type text/HTML */
1132 #define HTTPD_TYPE_OCTET  "application/octet-stream"    /*!< HTTP Content type octext-stream */
1133 
1134 /**
1135  * @brief   API to set the HTTP content type
1136  *
1137  * This API sets the 'Content Type' field of the response.
1138  * The default content type is 'text/html'.
1139  *
1140  * @note
1141  *  - This API is supposed to be called only from the context of
1142  *    a URI handler where httpd_req_t* request pointer is valid.
1143  *  - This API only sets the content type to this value. The type
1144  *    isn't sent out until any of the send APIs is executed.
1145  *  - Make sure that the lifetime of the type string is valid till
1146  *    send function is called.
1147  *
1148  * @param[in] r     The request being responded to
1149  * @param[in] type  The Content Type of the response
1150  *
1151  * @return
1152  *  - ESP_OK   : On success
1153  *  - ESP_ERR_INVALID_ARG : Null arguments
1154  *  - ESP_ERR_HTTPD_INVALID_REQ : Invalid request pointer
1155  */
1156 esp_err_t httpd_resp_set_type(httpd_req_t *r, const char *type);
1157 
1158 /**
1159  * @brief   API to append any additional headers
1160  *
1161  * This API sets any additional header fields that need to be sent in the response.
1162  *
1163  * @note
1164  *  - This API is supposed to be called only from the context of
1165  *    a URI handler where httpd_req_t* request pointer is valid.
1166  *  - The header isn't sent out until any of the send APIs is executed.
1167  *  - The maximum allowed number of additional headers is limited to
1168  *    value of max_resp_headers in config structure.
1169  *  - Make sure that the lifetime of the field value strings are valid till
1170  *    send function is called.
1171  *
1172  * @param[in] r     The request being responded to
1173  * @param[in] field The field name of the HTTP header
1174  * @param[in] value The value of this HTTP header
1175  *
1176  * @return
1177  *  - ESP_OK : On successfully appending new header
1178  *  - ESP_ERR_INVALID_ARG : Null arguments
1179  *  - ESP_ERR_HTTPD_RESP_HDR    : Total additional headers exceed max allowed
1180  *  - ESP_ERR_HTTPD_INVALID_REQ : Invalid request pointer
1181  */
1182 esp_err_t httpd_resp_set_hdr(httpd_req_t *r, const char *field, const char *value);
1183 
1184 /**
1185  * @brief   For sending out error code in response to HTTP request.
1186  *
1187  * @note
1188  *  - This API is supposed to be called only from the context of
1189  *    a URI handler where httpd_req_t* request pointer is valid.
1190  *  - Once this API is called, all request headers are purged, so
1191  *    request headers need be copied into separate buffers if
1192  *    they are required later.
1193  *  - If you wish to send additional data in the body of the
1194  *    response, please use the lower-level functions directly.
1195  *
1196  * @param[in] req     Pointer to the HTTP request for which the response needs to be sent
1197  * @param[in] error   Error type to send
1198  * @param[in] msg     Error message string (pass NULL for default message)
1199  *
1200  * @return
1201  *  - ESP_OK : On successfully sending the response packet
1202  *  - ESP_ERR_INVALID_ARG : Null arguments
1203  *  - ESP_ERR_HTTPD_RESP_SEND   : Error in raw send
1204  *  - ESP_ERR_HTTPD_INVALID_REQ : Invalid request pointer
1205  */
1206 esp_err_t httpd_resp_send_err(httpd_req_t *req, httpd_err_code_t error, const char *msg);
1207 
1208 /**
1209  * @brief   Helper function for HTTP 404
1210  *
1211  * Send HTTP 404 message. If you wish to send additional data in the body of the
1212  * response, please use the lower-level functions directly.
1213  *
1214  * @note
1215  *  - This API is supposed to be called only from the context of
1216  *    a URI handler where httpd_req_t* request pointer is valid.
1217  *  - Once this API is called, all request headers are purged, so
1218  *    request headers need be copied into separate buffers if
1219  *    they are required later.
1220  *
1221  * @param[in] r The request being responded to
1222  *
1223  * @return
1224  *  - ESP_OK : On successfully sending the response packet
1225  *  - ESP_ERR_INVALID_ARG : Null arguments
1226  *  - ESP_ERR_HTTPD_RESP_SEND   : Error in raw send
1227  *  - ESP_ERR_HTTPD_INVALID_REQ : Invalid request pointer
1228  */
httpd_resp_send_404(httpd_req_t * r)1229 static inline esp_err_t httpd_resp_send_404(httpd_req_t *r) {
1230     return httpd_resp_send_err(r, HTTPD_404_NOT_FOUND, NULL);
1231 }
1232 
1233 /**
1234  * @brief   Helper function for HTTP 408
1235  *
1236  * Send HTTP 408 message. If you wish to send additional data in the body of the
1237  * response, please use the lower-level functions directly.
1238  *
1239  * @note
1240  *  - This API is supposed to be called only from the context of
1241  *    a URI handler where httpd_req_t* request pointer is valid.
1242  *  - Once this API is called, all request headers are purged, so
1243  *    request headers need be copied into separate buffers if
1244  *    they are required later.
1245  *
1246  * @param[in] r The request being responded to
1247  *
1248  * @return
1249  *  - ESP_OK : On successfully sending the response packet
1250  *  - ESP_ERR_INVALID_ARG : Null arguments
1251  *  - ESP_ERR_HTTPD_RESP_SEND   : Error in raw send
1252  *  - ESP_ERR_HTTPD_INVALID_REQ : Invalid request pointer
1253  */
httpd_resp_send_408(httpd_req_t * r)1254 static inline esp_err_t httpd_resp_send_408(httpd_req_t *r) {
1255     return httpd_resp_send_err(r, HTTPD_408_REQ_TIMEOUT, NULL);
1256 }
1257 
1258 /**
1259  * @brief   Helper function for HTTP 500
1260  *
1261  * Send HTTP 500 message. If you wish to send additional data in the body of the
1262  * response, please use the lower-level functions directly.
1263  *
1264  * @note
1265  *  - This API is supposed to be called only from the context of
1266  *    a URI handler where httpd_req_t* request pointer is valid.
1267  *  - Once this API is called, all request headers are purged, so
1268  *    request headers need be copied into separate buffers if
1269  *    they are required later.
1270  *
1271  * @param[in] r The request being responded to
1272  *
1273  * @return
1274  *  - ESP_OK : On successfully sending the response packet
1275  *  - ESP_ERR_INVALID_ARG : Null arguments
1276  *  - ESP_ERR_HTTPD_RESP_SEND   : Error in raw send
1277  *  - ESP_ERR_HTTPD_INVALID_REQ : Invalid request pointer
1278  */
httpd_resp_send_500(httpd_req_t * r)1279 static inline esp_err_t httpd_resp_send_500(httpd_req_t *r) {
1280     return httpd_resp_send_err(r, HTTPD_500_INTERNAL_SERVER_ERROR, NULL);
1281 }
1282 
1283 /**
1284  * @brief   Raw HTTP send
1285  *
1286  * Call this API if you wish to construct your custom response packet.
1287  * When using this, all essential header, eg. HTTP version, Status Code,
1288  * Content Type and Length, Encoding, etc. will have to be constructed
1289  * manually, and HTTP delimeters (CRLF) will need to be placed correctly
1290  * for separating sub-sections of the HTTP response packet.
1291  *
1292  * If the send override function is set, this API will end up
1293  * calling that function eventually to send data out.
1294  *
1295  * @note
1296  *  - This API is supposed to be called only from the context of
1297  *    a URI handler where httpd_req_t* request pointer is valid.
1298  *  - Unless the response has the correct HTTP structure (which the
1299  *    user must now ensure) it is not guaranteed that it will be
1300  *    recognized by the client. For most cases, you wouldn't have
1301  *    to call this API, but you would rather use either of :
1302  *          httpd_resp_send(),
1303  *          httpd_resp_send_chunk()
1304  *
1305  * @param[in] r         The request being responded to
1306  * @param[in] buf       Buffer from where the fully constructed packet is to be read
1307  * @param[in] buf_len   Length of the buffer
1308  *
1309  * @return
1310  *  - Bytes : Number of bytes that were sent successfully
1311  *  - HTTPD_SOCK_ERR_INVALID  : Invalid arguments
1312  *  - HTTPD_SOCK_ERR_TIMEOUT  : Timeout/interrupted while calling socket send()
1313  *  - HTTPD_SOCK_ERR_FAIL     : Unrecoverable error while calling socket send()
1314  */
1315 int httpd_send(httpd_req_t *r, const char *buf, size_t buf_len);
1316 
1317 /**
1318  * A low level API to send data on a given socket
1319  *
1320  * @note This API is not recommended to be used in any request handler.
1321  * Use this only for advanced use cases, wherein some asynchronous
1322  * data is to be sent over a socket.
1323  *
1324  * This internally calls the default send function, or the function registered by
1325  * httpd_sess_set_send_override().
1326  *
1327  * @param[in] hd        server instance
1328  * @param[in] sockfd    session socket file descriptor
1329  * @param[in] buf       buffer with bytes to send
1330  * @param[in] buf_len   data size
1331  * @param[in] flags     flags for the send() function
1332  * @return
1333  *  - Bytes : The number of bytes sent successfully
1334  *  - HTTPD_SOCK_ERR_INVALID  : Invalid arguments
1335  *  - HTTPD_SOCK_ERR_TIMEOUT  : Timeout/interrupted while calling socket send()
1336  *  - HTTPD_SOCK_ERR_FAIL     : Unrecoverable error while calling socket send()
1337  */
1338 int httpd_socket_send(httpd_handle_t hd, int sockfd, const char *buf, size_t buf_len, int flags);
1339 
1340 /**
1341  * A low level API to receive data from a given socket
1342  *
1343  * @note This API is not recommended to be used in any request handler.
1344  * Use this only for advanced use cases, wherein some asynchronous
1345  * communication is required.
1346  *
1347  * This internally calls the default recv function, or the function registered by
1348  * httpd_sess_set_recv_override().
1349  *
1350  * @param[in] hd        server instance
1351  * @param[in] sockfd    session socket file descriptor
1352  * @param[in] buf       buffer with bytes to send
1353  * @param[in] buf_len   data size
1354  * @param[in] flags     flags for the send() function
1355  * @return
1356  *  - Bytes : The number of bytes received successfully
1357  *  - 0     : Buffer length parameter is zero / connection closed by peer
1358  *  - HTTPD_SOCK_ERR_INVALID  : Invalid arguments
1359  *  - HTTPD_SOCK_ERR_TIMEOUT  : Timeout/interrupted while calling socket recv()
1360  *  - HTTPD_SOCK_ERR_FAIL     : Unrecoverable error while calling socket recv()
1361  */
1362 int httpd_socket_recv(httpd_handle_t hd, int sockfd, char *buf, size_t buf_len, int flags);
1363 
1364 /** End of Request / Response
1365  * @}
1366  */
1367 
1368 /* ************** Group: Session ************** */
1369 /** @name Session
1370  * Functions for controlling sessions and accessing context data
1371  * @{
1372  */
1373 
1374 /**
1375  * @brief   Get session context from socket descriptor
1376  *
1377  * Typically if a session context is created, it is available to URI handlers
1378  * through the httpd_req_t structure. But, there are cases where the web
1379  * server's send/receive functions may require the context (for example, for
1380  * accessing keying information etc). Since the send/receive function only have
1381  * the socket descriptor at their disposal, this API provides them with a way to
1382  * retrieve the session context.
1383  *
1384  * @param[in] handle    Handle to server returned by httpd_start
1385  * @param[in] sockfd    The socket descriptor for which the context should be extracted.
1386  *
1387  * @return
1388  *  - void* : Pointer to the context associated with this session
1389  *  - NULL  : Empty context / Invalid handle / Invalid socket fd
1390  */
1391 void *httpd_sess_get_ctx(httpd_handle_t handle, int sockfd);
1392 
1393 /**
1394  * @brief   Set session context by socket descriptor
1395  *
1396  * @param[in] handle    Handle to server returned by httpd_start
1397  * @param[in] sockfd    The socket descriptor for which the context should be extracted.
1398  * @param[in] ctx       Context object to assign to the session
1399  * @param[in] free_fn   Function that should be called to free the context
1400  */
1401 void httpd_sess_set_ctx(httpd_handle_t handle, int sockfd, void *ctx, httpd_free_ctx_fn_t free_fn);
1402 
1403 /**
1404  * @brief   Get session 'transport' context by socket descriptor
1405  * @see     httpd_sess_get_ctx()
1406  *
1407  * This context is used by the send/receive functions, for example to manage SSL context.
1408  *
1409  * @param[in] handle    Handle to server returned by httpd_start
1410  * @param[in] sockfd    The socket descriptor for which the context should be extracted.
1411  * @return
1412  *  - void* : Pointer to the transport context associated with this session
1413  *  - NULL  : Empty context / Invalid handle / Invalid socket fd
1414  */
1415 void *httpd_sess_get_transport_ctx(httpd_handle_t handle, int sockfd);
1416 
1417 /**
1418  * @brief   Set session 'transport' context by socket descriptor
1419  * @see     httpd_sess_set_ctx()
1420  *
1421  * @param[in] handle    Handle to server returned by httpd_start
1422  * @param[in] sockfd    The socket descriptor for which the context should be extracted.
1423  * @param[in] ctx       Transport context object to assign to the session
1424  * @param[in] free_fn   Function that should be called to free the transport context
1425  */
1426 void httpd_sess_set_transport_ctx(httpd_handle_t handle, int sockfd, void *ctx, httpd_free_ctx_fn_t free_fn);
1427 
1428 /**
1429  * @brief   Get HTTPD global user context (it was set in the server config struct)
1430  *
1431  * @param[in] handle    Handle to server returned by httpd_start
1432  * @return global user context
1433  */
1434 void *httpd_get_global_user_ctx(httpd_handle_t handle);
1435 
1436 /**
1437  * @brief   Get HTTPD global transport context (it was set in the server config struct)
1438  *
1439  * @param[in] handle    Handle to server returned by httpd_start
1440  * @return global transport context
1441  */
1442 void *httpd_get_global_transport_ctx(httpd_handle_t handle);
1443 
1444 /**
1445  * @brief   Trigger an httpd session close externally
1446  *
1447  * @note    Calling this API is only required in special circumstances wherein
1448  *          some application requires to close an httpd client session asynchronously.
1449  *
1450  * @param[in] handle    Handle to server returned by httpd_start
1451  * @param[in] sockfd    The socket descriptor of the session to be closed
1452  *
1453  * @return
1454  *  - ESP_OK    : On successfully initiating closure
1455  *  - ESP_FAIL  : Failure to queue work
1456  *  - ESP_ERR_NOT_FOUND   : Socket fd not found
1457  *  - ESP_ERR_INVALID_ARG : Null arguments
1458  */
1459 esp_err_t httpd_sess_trigger_close(httpd_handle_t handle, int sockfd);
1460 
1461 /**
1462  * @brief   Update LRU counter for a given socket
1463  *
1464  * LRU Counters are internally associated with each session to monitor
1465  * how recently a session exchanged traffic. When LRU purge is enabled,
1466  * if a client is requesting for connection but maximum number of
1467  * sockets/sessions is reached, then the session having the earliest
1468  * LRU counter is closed automatically.
1469  *
1470  * Updating the LRU counter manually prevents the socket from being purged
1471  * due to the Least Recently Used (LRU) logic, even though it might not
1472  * have received traffic for some time. This is useful when all open
1473  * sockets/session are frequently exchanging traffic but the user specifically
1474  * wants one of the sessions to be kept open, irrespective of when it last
1475  * exchanged a packet.
1476  *
1477  * @note    Calling this API is only necessary if the LRU Purge Enable option
1478  *          is enabled.
1479  *
1480  * @param[in] handle    Handle to server returned by httpd_start
1481  * @param[in] sockfd    The socket descriptor of the session for which LRU counter
1482  *                      is to be updated
1483  *
1484  * @return
1485  *  - ESP_OK : Socket found and LRU counter updated
1486  *  - ESP_ERR_NOT_FOUND   : Socket not found
1487  *  - ESP_ERR_INVALID_ARG : Null arguments
1488  */
1489 esp_err_t httpd_sess_update_lru_counter(httpd_handle_t handle, int sockfd);
1490 
1491 /**
1492  * @brief   Returns list of current socket descriptors of active sessions
1493  *
1494  * @param[in] handle    Handle to server returned by httpd_start
1495  * @param[in,out] fds   In: Number of fds allocated in the supplied structure client_fds
1496  *                      Out: Number of valid client fds returned in client_fds,
1497  * @param[out] client_fds  Array of client fds
1498  *
1499  * @return
1500  *  - ESP_OK              : Successfully retrieved session list
1501  *  - ESP_ERR_INVALID_ARG : Wrong arguments or list is longer than allocated
1502  */
1503 esp_err_t httpd_get_client_list(httpd_handle_t handle, size_t *fds, int *client_fds);
1504 
1505 /** End of Session
1506  * @}
1507  */
1508 
1509 /* ************** Group: Work Queue ************** */
1510 /** @name Work Queue
1511  * APIs related to the HTTPD Work Queue
1512  * @{
1513  */
1514 
1515 /**
1516  * @brief   Prototype of the HTTPD work function
1517  *          Please refer to httpd_queue_work() for more details.
1518  * @param[in] arg   The arguments for this work function
1519  */
1520 typedef void (*httpd_work_fn_t)(void *arg);
1521 
1522 /**
1523  * @brief   Queue execution of a function in HTTPD's context
1524  *
1525  * This API queues a work function for asynchronous execution
1526  *
1527  * @note    Some protocols require that the web server generate some asynchronous data
1528  *          and send it to the persistently opened connection. This facility is for use
1529  *          by such protocols.
1530  *
1531  * @param[in] handle    Handle to server returned by httpd_start
1532  * @param[in] work      Pointer to the function to be executed in the HTTPD's context
1533  * @param[in] arg       Pointer to the arguments that should be passed to this function
1534  *
1535  * @return
1536  *  - ESP_OK   : On successfully queueing the work
1537  *  - ESP_FAIL : Failure in ctrl socket
1538  *  - ESP_ERR_INVALID_ARG : Null arguments
1539  */
1540 esp_err_t httpd_queue_work(httpd_handle_t handle, httpd_work_fn_t work, void *arg);
1541 
1542 /** End of Group Work Queue
1543  * @}
1544  */
1545 
1546 /* ************** Group: WebSocket ************** */
1547 /** @name WebSocket
1548  * Functions and structs for WebSocket server
1549  * @{
1550  */
1551 #ifdef CONFIG_HTTPD_WS_SUPPORT
1552 /**
1553  * @brief Enum for WebSocket packet types (Opcode in the header)
1554  * @note Please refer to RFC6455 Section 5.4 for more details
1555  */
1556 typedef enum {
1557     HTTPD_WS_TYPE_CONTINUE   = 0x0,
1558     HTTPD_WS_TYPE_TEXT       = 0x1,
1559     HTTPD_WS_TYPE_BINARY     = 0x2,
1560     HTTPD_WS_TYPE_CLOSE      = 0x8,
1561     HTTPD_WS_TYPE_PING       = 0x9,
1562     HTTPD_WS_TYPE_PONG       = 0xA
1563 } httpd_ws_type_t;
1564 
1565 /**
1566  * @brief Enum for client info description
1567  */
1568 typedef enum {
1569     HTTPD_WS_CLIENT_INVALID        = 0x0,
1570     HTTPD_WS_CLIENT_HTTP           = 0x1,
1571     HTTPD_WS_CLIENT_WEBSOCKET      = 0x2,
1572 } httpd_ws_client_info_t;
1573 
1574 /**
1575  * @brief WebSocket frame format
1576  */
1577 typedef struct httpd_ws_frame {
1578     bool final;                 /*!< Final frame:
1579                                      For received frames this field indicates whether the `FIN` flag was set.
1580                                      For frames to be transmitted, this field is only used if the `fragmented`
1581                                          option is set as well. If `fragmented` is false, the `FIN` flag is set
1582                                          by default, marking the ws_frame as a complete/unfragmented message
1583                                          (esp_http_server doesn't automatically fragment messages) */
1584     bool fragmented;            /*!< Indication that the frame allocated for transmission is a message fragment,
1585                                      so the `FIN` flag is set manually according to the `final` option.
1586                                      This flag is never set for received messages */
1587     httpd_ws_type_t type;       /*!< WebSocket frame type */
1588     uint8_t *payload;           /*!< Pre-allocated data buffer */
1589     size_t len;                 /*!< Length of the WebSocket data */
1590 } httpd_ws_frame_t;
1591 
1592 /**
1593  * @brief Receive and parse a WebSocket frame
1594  * @param[in]   req         Current request
1595  * @param[out]  pkt         WebSocket packet
1596  * @param[in]   max_len     Maximum length for receive
1597  * @return
1598  *  - ESP_OK                    : On successful
1599  *  - ESP_FAIL                  : Socket errors occurs
1600  *  - ESP_ERR_INVALID_STATE     : Handshake was already done beforehand
1601  *  - ESP_ERR_INVALID_ARG       : Argument is invalid (null or non-WebSocket)
1602  */
1603 esp_err_t httpd_ws_recv_frame(httpd_req_t *req, httpd_ws_frame_t *pkt, size_t max_len);
1604 
1605 /**
1606  * @brief Construct and send a WebSocket frame
1607  * @param[in]   req     Current request
1608  * @param[in]   pkt     WebSocket frame
1609  * @return
1610  *  - ESP_OK                    : On successful
1611  *  - ESP_FAIL                  : When socket errors occurs
1612  *  - ESP_ERR_INVALID_STATE     : Handshake was already done beforehand
1613  *  - ESP_ERR_INVALID_ARG       : Argument is invalid (null or non-WebSocket)
1614  */
1615 esp_err_t httpd_ws_send_frame(httpd_req_t *req, httpd_ws_frame_t *pkt);
1616 
1617 /**
1618  * @brief Low level send of a WebSocket frame out of the scope of current request
1619  * using internally configured httpd send function
1620  *
1621  * This API should rarely be called directly, with an exception of asynchronous send using httpd_queue_work.
1622  *
1623  * @param[in] hd      Server instance data
1624  * @param[in] fd      Socket descriptor for sending data
1625  * @param[in] frame     WebSocket frame
1626  * @return
1627  *  - ESP_OK                    : On successful
1628  *  - ESP_FAIL                  : When socket errors occurs
1629  *  - ESP_ERR_INVALID_STATE     : Handshake was already done beforehand
1630  *  - ESP_ERR_INVALID_ARG       : Argument is invalid (null or non-WebSocket)
1631  */
1632 esp_err_t httpd_ws_send_frame_async(httpd_handle_t hd, int fd, httpd_ws_frame_t *frame);
1633 
1634 /**
1635  * @brief Checks the supplied socket descriptor if it belongs to any active client
1636  * of this server instance and if the websoket protocol is active
1637  *
1638  * @param[in] hd      Server instance data
1639  * @param[in] fd      Socket descriptor
1640  * @return
1641  *  - HTTPD_WS_CLIENT_INVALID   : This fd is not a client of this httpd
1642  *  - HTTPD_WS_CLIENT_HTTP      : This fd is an active client, protocol is not WS
1643  *  - HTTPD_WS_CLIENT_WEBSOCKET : This fd is an active client, protocol is WS
1644  */
1645 httpd_ws_client_info_t httpd_ws_get_fd_info(httpd_handle_t hd, int fd);
1646 
1647 #endif /* CONFIG_HTTPD_WS_SUPPORT */
1648 /** End of WebSocket related stuff
1649  * @}
1650  */
1651 
1652 #ifdef __cplusplus
1653 }
1654 #endif
1655 
1656 #endif /* ! _ESP_HTTP_SERVER_H_ */
1657