Skip to content

Commit

Permalink
feat(httpd): add support for asynchronous request handling
Browse files Browse the repository at this point in the history
This commit adds support for handling multiple requests simultaneously by introducing two new functions: `httpd_req_async_handler_begin()` and `httpd_req_async_handler_complete()`. These functions allow creating an asynchronous copy of a request that can be used on a separate thread and marking the asynchronous request as completed, respectively.

Additionally, a new flag `for_async_req` has been added to the `httpd_sess_t` struct to indicate if a socket is being used for an asynchronous request and should not be purged from the LRU cache.

An example have been added to demonstrate the usage of these new functions.

Closes espressif/esp-idf#10594

Signed-off-by: Harshit Malpani <[email protected]>
  • Loading branch information
chipweinberger authored and hmalpani committed May 19, 2023
1 parent a977324 commit e03656e
Show file tree
Hide file tree
Showing 4 changed files with 87 additions and 4 deletions.
34 changes: 34 additions & 0 deletions include/esp_http_server.h
Original file line number Diff line number Diff line change
Expand Up @@ -812,6 +812,40 @@ esp_err_t httpd_sess_set_send_override(httpd_handle_t hd, int sockfd, httpd_send
*/
esp_err_t httpd_sess_set_pending_override(httpd_handle_t hd, int sockfd, httpd_pending_func_t pending_func);

/**
* @brief Start an asynchronous request. This function can be called
* in a request handler to get a request copy that can be used on a async thread.
*
* @note
* - This function is necessary in order to handle multiple requests simultaneously.
* See examples/async_requests for example usage.
* - You must call httpd_req_async_handler_complete() when you are done with the request.
*
* @param[in] r The request to create an async copy of
* @param[out] out A newly allocated request which can be used on an async thread
*
* @return
* - ESP_OK : async request object created
*/
esp_err_t httpd_req_async_handler_begin(httpd_req_t *r, httpd_req_t **out);

/**
* @brief Mark an asynchronous request as completed. This will
* - free the request memory
* - relinquish ownership of the underlying socket, so it can be reused.
* - allow the http server to close our socket if needed (lru_purge_enable)
*
* @note If async requests are not marked completed, eventually the server
* will no longer accept incoming connections. The server will log a
* "httpd_accept_conn: error in accept (23)" message if this happens.
*
* @param[in] r The request to mark async work as completed
*
* @return
* - ESP_OK : async request was marked completed
*/
esp_err_t httpd_req_async_handler_complete(httpd_req_t *r);

/**
* @brief Get the Socket Descriptor from the HTTP request
*
Expand Down
1 change: 1 addition & 0 deletions src/esp_httpd_priv.h
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ struct sock_db {
bool lru_socket; /*!< Flag indicating LRU socket */
char pending_data[PARSER_BLOCK_SIZE]; /*!< Buffer for pending data to be received */
size_t pending_len; /*!< Length of pending data to be received */
bool for_async_req; /*!< If true, the socket will not be LRU purged */
#ifdef CONFIG_HTTPD_WS_SUPPORT
bool ws_handshake_done; /*!< True if it has done WebSocket handshake (if this socket is a valid WS) */
bool ws_close; /*!< Set to true to close the socket later (when WS Close frame received) */
Expand Down
11 changes: 7 additions & 4 deletions src/httpd_sess.c
Original file line number Diff line number Diff line change
Expand Up @@ -107,10 +107,13 @@ static int enum_function(struct sock_db *session, void *context)
if (session->fd == -1) {
return 0;
}
// Check/update lowest lru
if (session->lru_counter < ctx->lru_counter) {
ctx->lru_counter = session->lru_counter;
ctx->session = session;
// Only close sockets that are not in use
if (session->for_async_req == false) {
// Check/update lowest lru
if (session->lru_counter < ctx->lru_counter) {
ctx->lru_counter = session->lru_counter;
ctx->session = session;
}
}
break;
case HTTPD_TASK_CLOSE:
Expand Down
45 changes: 45 additions & 0 deletions src/httpd_txrx.c
Original file line number Diff line number Diff line change
Expand Up @@ -554,6 +554,51 @@ int httpd_req_recv(httpd_req_t *r, char *buf, size_t buf_len)
return ret;
}

esp_err_t httpd_req_async_handler_begin(httpd_req_t *r, httpd_req_t **out)
{
if (r == NULL || out == NULL) {
return ESP_ERR_INVALID_ARG;
}

// alloc async req
httpd_req_t *async = malloc(sizeof(httpd_req_t));
if (async == NULL) {
return ESP_ERR_NO_MEM;
}
memcpy(async, r, sizeof(httpd_req_t));

// alloc async aux
async->aux = malloc(sizeof(struct httpd_req_aux));
if (async->aux == NULL) {
free(async);
return ESP_ERR_NO_MEM;
}
memcpy(async->aux, r->aux, sizeof(struct httpd_req_aux));

// mark socket as "in use"
struct httpd_req_aux *ra = r->aux;
ra->sd->for_async_req = true;

*out = async;

return ESP_OK;
}

esp_err_t httpd_req_async_handler_complete(httpd_req_t *r)
{
if (r == NULL) {
return ESP_ERR_INVALID_ARG;
}

struct httpd_req_aux *ra = r->aux;
ra->sd->for_async_req = false;

free(r->aux);
free(r);

return ESP_OK;
}

int httpd_req_to_sockfd(httpd_req_t *r)
{
if (r == NULL) {
Expand Down

0 comments on commit e03656e

Please sign in to comment.